```r

knitr::opts_chunk$set(warning=FALSE)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Needed libraries


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubGlicmFyeShkcGx5cilcbmxpYnJhcnkoY291bnRyeWNvZGUpXG5saWJyYXJ5KG91dGxpZXJzKVxubGlicmFyeShjYXJldClcbmxpYnJhcnkoY2x1c3RlcilcbmxpYnJhcnkoZmFjdG9leHRyYSlcbmxpYnJhcnkoTmJDbHVzdClcbmxpYnJhcnkoXFxETXdSXFwpXG5saWJyYXJ5KFxcUldla2FcXClcbmxpYnJhcnkoXFxDNTBcXClcbmxpYnJhcnkoXFxycGFydFxcKVxubGlicmFyeShcXHRoZW1pc1xcKVxubGlicmFyeShyYXR0bGUpXG5saWJyYXJ5KHJwYXJ0LnBsb3QpXG5saWJyYXJ5KFJDb2xvckJyZXdlcilcbmBgYFxuYGBgIn0= -->

```r
```r
library(dplyr)
library(countrycode)
library(outliers)
library(caret)
library(cluster)
library(factoextra)
library(NbClust)
library(\DMwR\)
library(\RWeka\)
library(\C50\)
library(\rpart\)
library(\themis\)
library(rattle)
library(rpart.plot)
library(RColorBrewer)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


# phase 1

### Problem statement

Prediction of cyber security employees' salaries based on 11 attributes & grouping employees based on shared characteristics.

1.work_year

2.experience_level

3.employment_type

4.job_title

5.salary

6.salary_currency

7.salary_in_usd

8.employee_residence

9.remote_ratio

10.company_location

11.company_size

### Problem description

We are living in the "information age" or rather the "data age", meaning that everything around us revolves around data. The data has become one of the most valuable assets that a person or an organisation can have, since it has a significant value, losing it will lead to significant damages. Thus, most of the attacks nowadays are directed toward the data. To guard against such damages, organisations have realised the importance of protecting their digital assets, leading them to hire cybersecurity specialists. This made cybersecurity gain popularity among people so there's a growing tendency to study cybersecurity. Consequently this resulted in the emergence of plentiful professionals with various experience levels and skills in this field. As a result, organisations may find it difficult to decide a salary for job candidates solely based on the CV. also, since the attacks improve rapidly, organisations need to hire more employees in the far future to defend against such attacks but it's not an easy matter to predict the future payroll which may hinders some of the organisation's plans. Another issue arises when the decision makers in the organisation aren't fully aware of the different groups of employees and their differint needs. Their lack of awareness gives a chance for the competitor organisations to attract their employees to them by offering a better salary and privilages that match their needs.

### Data mining task

Prediction of the cyber security employees' salary categories (Very Low, Low, , High, Very High) using classification, and description of data characteristics and behavior and grouping data using clustering methods.

### Goal

Given the problems we discussed and In order to better understand this field, we decided to analyse a dataset of 1247 cybersecurity employees, containing information such as salary, job title, and experience level. Analysing this dataset can provide insightful predictions regarding the salary range of a cybersecurity employee and description of the cybersecurity market behavior by grouping the data, which can help in:

-   Market segmentation
-   Identify trends
-   Specifying common charactrestics among cybersecurity employees
-   Identify the main cybersecurity employee groups for better understanding their needs
-   Making better decisions
-   Making recruitment and hiring process easier and more efficient
-   Predicting the future payroll
-   Increasing loyalty
-   Increasing the satisfaction rate
-   Achieving fairness
## Data

## Source of data:

<https://www.kaggle.com/datasets/deepcontractor/cyber-security-salaries>

### Reading and viewing dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZGF0YXNldD0gcmVhZC5jc3YodXJsKFxcaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1NhcmFoQWxoaW5kaS9ETV9wcm9qZWN0L21haW4vRGF0YSUyMFNldC9zYWxhcmllc19jeWJlci5jc3ZcXCksIGhlYWRlcj1UUlVFKVxuVmlldyhkYXRhc2V0KVxuXG5gYGBcbmBgYCJ9 -->

```r
```r
dataset= read.csv(url(\https://raw.githubusercontent.com/SarahAlhindi/DM_project/main/Data%20Set/salaries_cyber.csv\), header=TRUE)
View(dataset)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Original dataset

we will keep a copy of the original dataset before data preprocessing to use if needed at any time


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxub3JpZ2luYWxEYXRhc2V0PSBkYXRhc2V0XG5gYGBcbmBgYCJ9 -->

```r
```r
originalDataset= dataset

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


## General information about the dataset:

No. of attributes: 11\
Type of attributes: Ordinal , Nominal, and Numeric\
No. of objects: 1247\
Class label: salary_in_usd


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubmNvbChkYXRhc2V0KVxubnJvdyhkYXRhc2V0KVxubmFtZXMoZGF0YXNldClcbnN0cihkYXRhc2V0KVxuYGBgXG5gYGAifQ== -->

```r
```r
ncol(dataset)
nrow(dataset)
names(dataset)
str(dataset)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Attributes' description table

+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| **Attribute Name** | **Description**                                             | **Data Type** | **Possible values**                                       |
+====================+=============================================================+===============+===========================================================+
| work_year          | The year in which salary was recorded                       | Numerical     | 2020 to 2022                                              |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| experience_level   | Expertise level of the employee                             | Ordinal       | En "Entry level"\                                         |
|                    |                                                             |               | MI "Mid level"\                                           |
|                    |                                                             |               | SE "Senior level"\                                        |
|                    |                                                             |               | EX "Executive level"                                      |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| employment_type    | The nature or category of employee's engagement in the job  | Nominal       | PT "Part time"\                                           |
|                    |                                                             |               | FT "Full time"\                                           |
|                    |                                                             |               | CT "Contract\                                             |
|                    |                                                             |               | FL"Freelancer"                                            |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| job_title          | The role worked in during the year                          | Nominal       | Different titles.                                         |
|                    |                                                             |               |                                                           |
|                    |                                                             |               | like Security Analyst, security researcher                |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| salary             | The total gross salary amount paid                          | Numerical     | 1740-50001566                                             |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| salary_currency    | The currency of the salary paid to the employee             | Nominal       | Different currencies according to ISO 4217 currency code. |
|                    |                                                             |               |                                                           |
|                    |                                                             |               | like DE,CA                                                |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| salary_in_usd      | The salary paid in United states dollar                     | Numerical     | 2000 to 365596.40                                         |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| employee_residence | Employee's primary country of residence                     | Nominal       | Different countries.                                      |
|                    |                                                             |               |                                                           |
|                    |                                                             |               | like US,AE                                                |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| remote_ratio       | Percentage of online work by employee in the specified year | Numerical     | 0 "No remote work"\                                       |
|                    |                                                             |               | 50 "Partially remote"\                                    |
|                    |                                                             |               | 100 "Fully remote"                                        |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| company_location   | The country of the employer's main office                   | Nominal       | Different countries.                                      |
|                    |                                                             |               |                                                           |
|                    |                                                             |               | like BR,BW                                                |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+
| company_size       | How big/small is the company                                | Ordinal       | S , M or L                                                |
+--------------------+-------------------------------------------------------------+---------------+-----------------------------------------------------------+

# phase 2

### sample of 20 employees from the dataset:

using sample_n(table,size) function and using (set_seed())


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMzApXG5zYW1wbGU9c2FtcGxlX24oZGF0YXNldCwyMClcbnByaW50KHNhbXBsZSlcbmBgYFxuYGBgIn0= -->

```r
```r
set.seed(30)
sample=sample_n(dataset,20)
print(sample)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Show the missing value:

if it is FALSE it means no null value,if it is TRUE there is null value. In our dataset there is no null values.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuaXMubmEoZGF0YXNldClcbnN1bShpcy5uYShkYXRhc2V0KSlcbmBgYFxuYGBgIn0= -->

```r
```r
is.na(dataset)
sum(is.na(dataset))

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Show the Min.,1st Qu.,Median,Mean ,3rd Qu.,Max. for each numeric column

The summary statistics for the dataset variables provide insights into the distribution of features. we can conclude the following:

In work_year: The data spans from the year 2020 to 2022 with Most data falling within the years 2021 and 2022, as indicated by both the median and mean being centered around 2021.

In salary: Salaries vary widely with a minimum of \$1,740 and a maximum of \$500 million. The median is \$120,000 which is a mid value, but the mean is notably higher at \$560,852 which might be duo to extreme values or notable skewness.

In salary_in_usd: The data has a median of \$110,000, and a mean of \$120,278, and the spread of salaries is observable in the difference between the median and mean.

In remote_ratio: Indicates the percentage of remote work ranging from 0% to 100%, with a median and 3rd quartile at 100%, and a mean of 71.49%, indicating a notable presence of remote work in the dataset, suggesting some variability.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc3VtbWFyeShkYXRhc2V0JHdvcmtfeWVhcilcbnN1bW1hcnkoZGF0YXNldCRzYWxhcnkpXG5zdW1tYXJ5KGRhdGFzZXQkc2FsYXJ5X2luX3VzZClcbnN1bW1hcnkoZGF0YXNldCRyZW1vdGVfcmF0aW8pXG5gYGBcbmBgYCJ9 -->

```r
```r
summary(dataset$work_year)
summary(dataset$salary)
summary(dataset$salary_in_usd)
summary(dataset$remote_ratio)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Show the variane of each numeric column

variance is to understand the spread or dispersion of the values in each column. A higher variance indicates that the values are more spread out from the mean and in our dataset the highest varied attribute is salary, while a lower variance indicates that the values are closer to the mean which in our datas it is work year attribute.

Variance results reveal that: -work years are to some extent consistent -salaries show notable variability and possible outliers -salaries in USD have a stable distribution -remote work ratio have moderate variability


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxudmFyKGRhdGFzZXQkd29ya195ZWFyKVxudmFyKGRhdGFzZXQkc2FsYXJ5KVxudmFyKGRhdGFzZXQkc2FsYXJ5X2luX3VzZClcbnZhcihkYXRhc2V0JHJlbW90ZV9yYXRpbylcbmBgYFxuYGBgIn0= -->

```r
```r
var(dataset$work_year)
var(dataset$salary)
var(dataset$salary_in_usd)
var(dataset$remote_ratio)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Visualization of relationship between some pairs of attributes:

Here we used boxplot to see the distribution between salary_in_usd and experience_level We observed that salaries vary depending on the level of experience,they are positively correlated.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYm94cGxvdChzYWxhcnlfaW5fdXNkIH4gZXhwZXJpZW5jZV9sZXZlbCwgZGF0YSA9IGRhdGFzZXQgLCB5YXh0PVxcblxcKVxubGFiZWxzPC0gcHJldHR5KGRhdGFzZXQkc2FsYXJ5X2luX3VzZClcbmxhYmVsczwtIHNhcHBseShsYWJlbHMsIGZ1bmN0aW9uKHgpIGZvcm1hdCh4LCBzY2llbnRpZmljID0gRkFMU0UpKVxuYXhpcyhzaWRlID0gMiwgYXQ9cHJldHR5KGRhdGFzZXQkc2FsYXJ5X2luX3VzZCksIGxhYmVscyA9IGxhYmVscyApXG5vcHRpb25zKHNjaXBlbiA9IDk5OSlcbmBgYFxuYGBgIn0= -->

```r
```r
boxplot(salary_in_usd ~ experience_level, data = dataset , yaxt=\n\)
labels<- pretty(dataset$salary_in_usd)
labels<- sapply(labels, function(x) format(x, scientific = FALSE))
axis(side = 2, at=pretty(dataset$salary_in_usd), labels = labels )
options(scipen = 999)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


Here we used boxplot to see the distribution between salary_in_usd and work_year We observed that 2021 salaries were close to each other but in 2022 the gap between them getting bigger.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYm94cGxvdChzYWxhcnlfaW5fdXNkIH4gd29ya195ZWFyLCBkYXRhID0gZGF0YXNldCAsIHlheHQ9XFxuXFwpXG5sYWJlbHM8LSBwcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKVxubGFiZWxzPC0gc2FwcGx5KGxhYmVscywgZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBGQUxTRSkpXG5heGlzKHNpZGUgPSAyLCBhdD1wcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKSwgbGFiZWxzID0gbGFiZWxzIClcbm9wdGlvbnMoc2NpcGVuID0gOTk5KVxuYGBgXG5gYGAifQ== -->

```r
```r
boxplot(salary_in_usd ~ work_year, data = dataset , yaxt=\n\)
labels<- pretty(dataset$salary_in_usd)
labels<- sapply(labels, function(x) format(x, scientific = FALSE))
axis(side = 2, at=pretty(dataset$salary_in_usd), labels = labels )
options(scipen = 999)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


Here we used boxplot to see the distribution between salary_in_usd and employment_type We observed that Full Time (FT) offers more salary than the other categories.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYm94cGxvdChzYWxhcnlfaW5fdXNkIH4gZW1wbG95bWVudF90eXBlLCBkYXRhID0gZGF0YXNldCAsIHlheHQ9XFxuXFwpXG5sYWJlbHM8LSBwcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKVxubGFiZWxzPC0gc2FwcGx5KGxhYmVscywgZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBGQUxTRSkpXG5heGlzKHNpZGUgPSAyLCBhdD1wcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKSwgbGFiZWxzID0gbGFiZWxzIClcbm9wdGlvbnMoc2NpcGVuID0gOTk5KVxuYGBgXG5gYGAifQ== -->

```r
```r
boxplot(salary_in_usd ~ employment_type, data = dataset , yaxt=\n\)
labels<- pretty(dataset$salary_in_usd)
labels<- sapply(labels, function(x) format(x, scientific = FALSE))
axis(side = 2, at=pretty(dataset$salary_in_usd), labels = labels )
options(scipen = 999)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


Here we used boxplot to see the distribution between salary_in_usd and company_size We observed that the larger the company is the higher the salary was.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYm94cGxvdChzYWxhcnlfaW5fdXNkIH4gY29tcGFueV9zaXplLCBkYXRhID0gZGF0YXNldCAsIHlheHQ9XFxuXFwpXG5sYWJlbHM8LSBwcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKVxubGFiZWxzPC0gc2FwcGx5KGxhYmVscywgZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBGQUxTRSkpXG5heGlzKHNpZGUgPSAyLCBhdD1wcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKSwgbGFiZWxzID0gbGFiZWxzIClcbm9wdGlvbnMoc2NpcGVuID0gOTk5KSBcbmBgYFxuYGBgIn0= -->

```r
```r
boxplot(salary_in_usd ~ company_size, data = dataset , yaxt=\n\)
labels<- pretty(dataset$salary_in_usd)
labels<- sapply(labels, function(x) format(x, scientific = FALSE))
axis(side = 2, at=pretty(dataset$salary_in_usd), labels = labels )
options(scipen = 999) 

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


## Data preproccessing

## Data Reduction

### Dimensionality Reduction

The "salary" column gives the same information as "salary_in_usd" it's just a matter of currency exchange, and we will eventually transform all the values in "salary" column to one common currency so we can properly deal with them. To further confirm that the two column are redundant, we will use the latest exchange rate for USD to the desired currency.

we will start by creating a temporary column named "converted_salary" to save the salary that we will get by using the exchange rate to convert the salary_in_usd to the salary with different currencies to compare with "salary" column


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuY29udmVydGVkRGF0YXNldD1kYXRhc2V0XG5cblxuY29udmVydGVkRGF0YXNldCRleGNoYW5nZV9yYXRlID0gZmFjdG9yKGNvbnZlcnRlZERhdGFzZXQkc2FsYXJ5X2N1cnJlbmN5LCBsZXZlbHM9YyhcXFVTRFxcLFxcQlJMXFwsXFxHQlBcXCxcXEVVUlxcLFxcSU5SXFwsXFxDQURcXCxcXENIRlxcLFxcREtLXFwsXFxTR0RcXCxcXEFVRFxcLFxcU0VLXFwsXFxNWE5cXCxcXElMU1xcLFxcUExOXFwsXFxOT0tcXCxcXElEUlxcLFxcTlpEXFwsXFxIVUZcXCxcXFpBUlxcLFxcVFdEXFwsXFxSVUJcXCksIGxhYmVscz1jKDEvMSwxLzAuMjAsMS8xLjIyLDEvMS4wNiwxLzAuMDEyLDEvMC43NCwxLzEuMTAsMS8wLjE0LDEvMC43MywxLzAuNjQsMS8wLjA5MCwxLzAuMDU3LDEvMC4yNiwxLzAuMjMsMS8wLjA5MywxLzAuMDAwMDY1LDEvMC42MCwxLzAuMDAyNywxLzAuMDUzLDEvMC4wMzEsMS8wLjAxMCkpXG5jb252ZXJ0ZWREYXRhc2V0JGV4Y2hhbmdlX3JhdGUgPSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcihjb252ZXJ0ZWREYXRhc2V0JGV4Y2hhbmdlX3JhdGUpKVxuY29udmVydGVkRGF0YXNldCRjb252ZXJ0ZWRfc2FsYXJ5ID0gY29udmVydGVkRGF0YXNldCRzYWxhcnlfaW5fdXNkKmNvbnZlcnRlZERhdGFzZXQkZXhjaGFuZ2VfcmF0ZVxuXG5cblxuc2V0LnNlZWQoMSlcbnNhbGFyeV9zYW1wbGUgPC0gc2FtcGxlX24oY29udmVydGVkRGF0YXNldFssYyhcXHNhbGFyeVxcLFxcY29udmVydGVkX3NhbGFyeVxcKV0sMTApXG5cbnByaW50KHNhbGFyeV9zYW1wbGUpXG5gYGBcbmBgYCJ9 -->

```r
```r
convertedDataset=dataset


convertedDataset$exchange_rate = factor(convertedDataset$salary_currency, levels=c(\USD\,\BRL\,\GBP\,\EUR\,\INR\,\CAD\,\CHF\,\DKK\,\SGD\,\AUD\,\SEK\,\MXN\,\ILS\,\PLN\,\NOK\,\IDR\,\NZD\,\HUF\,\ZAR\,\TWD\,\RUB\), labels=c(1/1,1/0.20,1/1.22,1/1.06,1/0.012,1/0.74,1/1.10,1/0.14,1/0.73,1/0.64,1/0.090,1/0.057,1/0.26,1/0.23,1/0.093,1/0.000065,1/0.60,1/0.0027,1/0.053,1/0.031,1/0.010))
convertedDataset$exchange_rate = as.numeric(as.character(convertedDataset$exchange_rate))
convertedDataset$converted_salary = convertedDataset$salary_in_usd*convertedDataset$exchange_rate



set.seed(1)
salary_sample <- sample_n(convertedDataset[,c(\salary\,\converted_salary\)],10)

print(salary_sample)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


as shown in the sample, the two columns are almost identical. This can be proved by correlation coefficient as well.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuY29ycmVsYXRpb24gPC0gY29yKGNvbnZlcnRlZERhdGFzZXQkc2FsYXJ5ICwgY29udmVydGVkRGF0YXNldCRjb252ZXJ0ZWRfc2FsYXJ5KVxucHJpbnQoY29ycmVsYXRpb24pXG5gYGBcbmBgYCJ9 -->

```r
```r
correlation <- cor(convertedDataset$salary , convertedDataset$converted_salary)
print(correlation)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


The correlation is so high but it hasn't reached 100% possibly due to rounding in the calculations and slight differences in the exchange rate over time.

To make the mining process more effiecent and has an improved quality, we decided to remove the "salary" column.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZGF0YXNldDwtZGF0YXNldFssLWMoNSldXG5gYGBcbmBgYCJ9 -->

```r
```r
dataset<-dataset[,-c(5)]

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Find the outliers and remove them:

We will show outliers with boxPlots and then remove them, to minimize noise and to get better analytical results when applying data mining techniques.

now we show (salary_in_usd) attributes' outliers. we can see that there are many outliers with exceptionally high values, thus we will remove them.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYm94cGxvdChkYXRhc2V0JHNhbGFyeV9pbl91c2QpXG5cblxuXG5PdXRTYWxhcnkgPSBvdXRsaWVyKGRhdGFzZXQkc2FsYXJ5X2luX3VzZCwgbG9naWNhbCA9VFJVRSlcbkZpbmRfb3V0bGllciA9IHdoaWNoKE91dFNhbGFyeSA9PVRSVUUsIGFyci5pbmQgPSBUUlVFKVxuZGF0YXNldD0gZGF0YXNldFstRmluZF9vdXRsaWVyLF1cblxuYGBgXG5gYGAifQ== -->

```r
```r
boxplot(dataset$salary_in_usd)



OutSalary = outlier(dataset$salary_in_usd, logical =TRUE)
Find_outlier = which(OutSalary ==TRUE, arr.ind = TRUE)
dataset= dataset[-Find_outlier,]

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


now we show (remote_ratio) attributes' outliers. we can see there aren't outliers in remote_ratio, thus we don't need the last step i.e: removing outliers' rows.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYm94cGxvdChkYXRhc2V0JHJlbW90ZV9yYXRpbylcblxuYGBgXG5gYGAifQ== -->

```r
```r
boxplot(dataset$remote_ratio)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


now we show (work_year) attributes' outliers. we can see there aren't outliers in work_year, thus we don't need the last step i.e: removing outliers' rows.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYm94cGxvdChkYXRhc2V0JHdvcmtfeWVhcilcblxuYGBgXG5gYGAifQ== -->

```r
```r
boxplot(dataset$work_year)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Concept hierarchy generation for nominal data

the columns "company_location" and "employee_residence" have the name of countries for the company and employee respectively. And these attributes can be generalized to higher-level concept that is region to help understand and analyze the dataset better and improve algorithm performance.

We will use the 7 regions as defined in the World Bank Development Indicators. These regions are:

1.  East Asia and Pacific: This region includes countries like China, Australia, Indonesia, Thailand, etc.

2.  Europe and Central Asia: This region includes countries like Germany, UK, Russia, Turkey, etc.

3.  Latin America & Caribbean: This region includes countries like Brazil, Mexico, Argentina, Cuba, etc.

4.  Middle East and North Africa: This region includes countries like Saudi Arabia, Egypt, Iran, Iraq, etc.

5.  North America: This is predominantly United States and Canada.

6.  South Asia: This region includes countries like India, Pakistan, Bangladesh, Sri Lanka, etc.

7.  Sub-Saharan Africa: This region includes countries like Nigeria, South Africa, Ethiopia, Kenya, etc.

Note: UM(The United States Minor Outlying Islands) and AQ(Antarctica) don't belong to any of these regions, thus, they will be used as they are.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5cbnVtPXdoaWNoKGRhdGFzZXQkY29tcGFueV9sb2NhdGlvbj09XFxVTVxcKVxuYXE9d2hpY2goZGF0YXNldCRjb21wYW55X2xvY2F0aW9uPT1cXEFRXFwpXG5cblxuZGF0YXNldCRjb21wYW55X2xvY2F0aW9uIDwtIGNvdW50cnljb2RlKGRhdGFzZXQkY29tcGFueV9sb2NhdGlvbiwgXFxpc28yY1xcLCBcXHJlZ2lvblxcKVxuZGF0YXNldCRlbXBsb3llZV9yZXNpZGVuY2UgPC0gY291bnRyeWNvZGUoZGF0YXNldCRlbXBsb3llZV9yZXNpZGVuY2UsIFxcaXNvMmNcXCwgXFxyZWdpb25cXClcblxuZGF0YXNldFt1bSxcXGNvbXBhbnlfbG9jYXRpb25cXF09XFxVTVxcXG5kYXRhc2V0W2FxLFxcY29tcGFueV9sb2NhdGlvblxcXT1cXEFRXFxcblxuYGBgXG5gYGAifQ== -->

```r
```r


um=which(dataset$company_location==\UM\)
aq=which(dataset$company_location==\AQ\)


dataset$company_location <- countrycode(dataset$company_location, \iso2c\, \region\)
dataset$employee_residence <- countrycode(dataset$employee_residence, \iso2c\, \region\)

dataset[um,\company_location\]=\UM\
dataset[aq,\company_location\]=\AQ\

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


Concept hierarchy generation can be done for "job_title" as well to improve interpretation and scalability. Also, most job titles are essentially the same job but with different names, so we can combine them into a higher-level jobs titles such as Architect, Analyst and Engineer etc.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuIyMgQ3JlYXRlIHRoZSBjYXRlZ29yaWVzIGJhc2VkIG9uIGpvYiByYW5rIFxuZGF0YXNldCRqb2JfdGl0bGUgPC0gaWZlbHNlKGdyZXBsKFxcQW5hbHlzdFxcLCBkYXRhc2V0JGpvYl90aXRsZSksIFxcQW5hbHlzdFxcLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoXFxBcmNoaXRlY3RcXCwgZGF0YXNldCRqb2JfdGl0bGUpLCBcXEFyY2hpdGVjdFxcLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKFxcRW5naW5lZXJcXCwgZGF0YXNldCRqb2JfdGl0bGUpLCBcXEVuZ2luZWVyXFwsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKFxcTWFuYWdlcnxPZmZpY2VyfERpcmVjdG9yfExlYWRlclxcLCBkYXRhc2V0JGpvYl90aXRsZSksIFxcTGVhZGVyc2hpcFxcLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoXFxDb25zdWx0YW50fFNwZWNpYWxpc3RcXCwgZGF0YXNldCRqb2JfdGl0bGUpLCBcXENvbnN1bHRhbnQvU3BlY2lhbGlzdFxcLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKFxcQ3liZXJcXCwgZGF0YXNldCRqb2JfdGl0bGUpLCBcXEN5YmVyIFNlY3VyaXR5XFwsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxPdGhlcnNcXCkpKSkpKVxuXG5gYGBcbmBgYCJ9 -->

```r
```r
## Create the categories based on job rank 
dataset$job_title <- ifelse(grepl(\Analyst\, dataset$job_title), \Analyst\,
                                ifelse(grepl(\Architect\, dataset$job_title), \Architect\,
                                       ifelse(grepl(\Engineer\, dataset$job_title), \Engineer\,
                                              ifelse(grepl(\Manager|Officer|Director|Leader\, dataset$job_title), \Leadership\,
                                                     ifelse(grepl(\Consultant|Specialist\, dataset$job_title), \Consultant/Specialist\,
                                                            ifelse(grepl(\Cyber\, dataset$job_title), \Cyber Security\,
                                                                   \Others\))))))

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


## Encoding categorical data

To deal with columns with character type we are going to encode them, because most machine learning algorithms are designed to work with factors data rather than character data and it improves performance and Interpretability of data as well.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZGF0YXNldCRqb2JfdGl0bGUgIDwtIGZhY3RvcihkYXRhc2V0JGpvYl90aXRsZSlcblxuZGF0YXNldCRleHBlcmllbmNlX2xldmVsID0gZmFjdG9yKGRhdGFzZXQkZXhwZXJpZW5jZV9sZXZlbCwgbGV2ZWxzPWMoXFxFTlxcLCBcXE1JXFwsIFxcU0VcXCwgXFxFWFxcKSwgbGFiZWxzPWMoMSwyLDMsNCkpXG5cbmRhdGFzZXQkZW1wbG95bWVudF90eXBlICA8LSBmYWN0b3IoZGF0YXNldCRlbXBsb3ltZW50X3R5cGUpXG5cbmRhdGFzZXQkZW1wbG95ZWVfcmVzaWRlbmNlICA8LSBmYWN0b3IoZGF0YXNldCRlbXBsb3llZV9yZXNpZGVuY2UpXG5cbmRhdGFzZXQkY29tcGFueV9sb2NhdGlvbiAgPC0gZmFjdG9yKGRhdGFzZXQkY29tcGFueV9sb2NhdGlvbilcblxuZGF0YXNldCRzYWxhcnlfY3VycmVuY3kgIDwtIGZhY3RvcihkYXRhc2V0JHNhbGFyeV9jdXJyZW5jeSlcblxuZGF0YXNldCRqb2JfdGl0bGUgIDwtIGZhY3RvcihkYXRhc2V0JGpvYl90aXRsZSlcblxuXG5kYXRhc2V0JGNvbXBhbnlfc2l6ZSA9IGZhY3RvcihkYXRhc2V0JGNvbXBhbnlfc2l6ZSwgbGV2ZWxzPWMoXFxTXFwsXFxNXFwsXFxMXFwpLCBsYWJlbHM9YygxLDIsMykpXG5cblxuZGF0YXNldCRqb2JfdGl0bGUgIDwtIGZhY3RvcihkYXRhc2V0JGpvYl90aXRsZSlcblxuYGBgXG5gYGAifQ== -->

```r
```r
dataset$job_title  <- factor(dataset$job_title)

dataset$experience_level = factor(dataset$experience_level, levels=c(\EN\, \MI\, \SE\, \EX\), labels=c(1,2,3,4))

dataset$employment_type  <- factor(dataset$employment_type)

dataset$employee_residence  <- factor(dataset$employee_residence)

dataset$company_location  <- factor(dataset$company_location)

dataset$salary_currency  <- factor(dataset$salary_currency)

dataset$job_title  <- factor(dataset$job_title)


dataset$company_size = factor(dataset$company_size, levels=c(\S\,\M\,\L\), labels=c(1,2,3))


dataset$job_title  <- factor(dataset$job_title)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Discretization of salaray_in_usd attribute

by calculating breaks based on quartiles


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYnJlYWtzIDwtIHF1YW50aWxlKGRhdGFzZXQkc2FsYXJ5X2luX3VzZCwgXG4gICAgICAgICAgICAgICAgICAgcHJvYnMgPSBjKDAsIC4yNSwgLjUsIC43NSwgLjk1LCAxKSwgXG4gICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKVxuXG5cbmRhdGFzZXQkc2FsYXJ5X2luX3VzZCA8LSBjdXQoZGF0YXNldCRzYWxhcnlfaW5fdXNkLCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGJyZWFrcywgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoXFxWZXJ5IExvd1xcLCBcXExvd1xcLCBcXE1lZGl1bVxcLCBcXEhpZ2hcXCwgXFxWZXJ5IEhpZ2hcXCkpXG5cblxuYGBgXG5gYGAifQ== -->

```r
```r
breaks <- quantile(dataset$salary_in_usd, 
                   probs = c(0, .25, .5, .75, .95, 1), 
                   na.rm = TRUE)


dataset$salary_in_usd <- cut(dataset$salary_in_usd, 
                                       breaks = breaks, 
                                       include.lowest = TRUE, 
                                       labels=c(\Very Low\, \Low\, \Medium\, \High\, \Very High\))

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


### Normalization:

to change the scale of numeric attributes (remote_ratio and work_year) to a scale of [-1,1] to give them equal weight


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZGF0YXNldCBbLCBjKFxcd29ya195ZWFyXFwgLCBcXHJlbW90ZV9yYXRpb1xcKV0gPSBzY2FsZShkYXRhc2V0IFssIGMoXFx3b3JrX3llYXJcXCAsIFxccmVtb3RlX3JhdGlvXFwpXSlcbmBgYFxuYGBgIn0= -->

```r
```r
dataset [, c(\work_year\ , \remote_ratio\)] = scale(dataset [, c(\work_year\ , \remote_ratio\)])

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


## Feature Selection

we will implement feature selection to remove redundant or irrelevant attributes from the data set to get the smallest subset that can help us get the most accurate predictions for our target class(salary_in_usd) and decrease the time that it takes the classifier to process the data.

we will use RFE(Recursive feature elimination) which is a wrapper method for the feature selection. Since the RFE function have multiple control options we need to specify the options that we want. We will choose "Random Forest" because it has high accuracy, can handle categorical data.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuY29udHJvbCA8LSByZmVDb250cm9sKGZ1bmN0aW9ucyA9IHJmRnVuY3MsIFxuICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9IFxccmVwZWF0ZWRjdlxcLFxuICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdHMgPSA1LCBcbiAgICAgICAgICAgICAgICAgICAgICBudW1iZXIgPSAxMClcbmBgYFxuYGBgIn0= -->

```r
```r
control <- rfeControl(functions = rfFuncs, 
                      method = \repeatedcv\,
                      repeats = 5, 
                      number = 10)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


First we save the features to be used in the feature selection(every attributes except the class label "salary_in_usd") in variable x, and the class label in variable y. Then split the data to 80% training and 20% test.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxueCA8LSBkYXRhc2V0ICU+JVxuICBzZWxlY3QoLXNhbGFyeV9pbl91c2QpICU+JVxuICBhcy5kYXRhLmZyYW1lKClcblxuIyBUYXJnZXQgdmFyaWFibGVcbnkgPC0gZGF0YXNldCRzYWxhcnlfaW5fdXNkXG5cbiMgVHJhaW5pbmc6IDgwJTsgVGVzdDogMjAlXG5zZXQuc2VlZCgyMDIxKVxuaW5UcmFpbiA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHksIHAgPSAuODAsIGxpc3QgPSBGQUxTRSlbLDFdXG5cbnhfdHJhaW4gPC0geFsgaW5UcmFpbiwgXVxueF90ZXN0ICA8LSB4Wy1pblRyYWluLCBdXG5cbnlfdHJhaW4gPC0geVsgaW5UcmFpbl1cbnlfdGVzdCAgPC0geVstaW5UcmFpbl1cblxuYGBgXG5gYGAifQ== -->

```r
```r
x <- dataset %>%
  select(-salary_in_usd) %>%
  as.data.frame()

# Target variable
y <- dataset$salary_in_usd

# Training: 80%; Test: 20%
set.seed(2021)
inTrain <- createDataPartition(y, p = .80, list = FALSE)[,1]

x_train <- x[ inTrain, ]
x_test  <- x[-inTrain, ]

y_train <- y[ inTrain]
y_test  <- y[-inTrain]

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


after splitting the data, now we can perform the selection using rfe


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMSlcbnJlc3VsdF9yZmUxIDwtIHJmZSh4ID0geF90cmFpbiwgXG4gICAgICAgICAgICAgICAgICAgeSA9IHlfdHJhaW4sIFxuICAgICAgICAgICAgICAgICAgIHNpemVzID0gYygxOjkpLFxuICAgICAgICAgICAgICAgICAgIHJmZUNvbnRyb2wgPSBjb250cm9sKVxuXG5yZXN1bHRfcmZlMVxuXG5wcmVkaWN0b3JzKHJlc3VsdF9yZmUxKVxuXG5gYGBcbmBgYCJ9 -->

```r
```r
set.seed(1)
result_rfe1 <- rfe(x = x_train, 
                   y = y_train, 
                   sizes = c(1:9),
                   rfeControl = control)

result_rfe1

predictors(result_rfe1)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


The results show that all the remaining attributes, except for "employment_type", are selected. This is logical, as 98% of the rows have the value "FT", as shown in the table below. Due to the low variance, we decided to remove this attribute.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxudGFibGUoZGF0YXNldCRlbXBsb3ltZW50X3R5cGUpXG5gYGBcbmBgYCJ9 -->

```r
```r
table(dataset$employment_type)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->



<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZGF0YXNldDwtZGF0YXNldFssLXdoaWNoKG5hbWVzKGRhdGFzZXQpPT1cXGVtcGxveW1lbnRfdHlwZVxcKV1cbmBgYFxuYGBgIn0= -->

```r
```r
dataset<-dataset[,-which(names(dataset)==\employment_type\)]

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


# phase 3

During this phase, our focus will be on clustering and classification techniques to analyze the data. The primary objectives are to identify distinct groups within the dataset through clustering, classify data objects into meaningful categories, and apply different evaluation methods to assess the accuracy and precision of both classification and clustering results. We aim to gain deeper insights into the data and discover patterns.

## Retreive our preprocessed dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG4jIFJlYWQgdGhlIENTViBmaWxlIGZyb20gZ2l0aHViXG5kYXRhc2V0Mj0gcmVhZC5jc3YodXJsKFxcaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1NhcmFoQWxoaW5kaS9ETV9wcm9qZWN0L21haW4vRGF0YSUyMFNldC9wcmVwcm9jZXNzZWREYXRhc2V0LmNzdlxcKSwgaGVhZGVyPVRSVUUpXG5cbiMgSWRlbnRpZnkgdGhlIGNoYXJhY3RlciB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQyXG5jaGFyX3ZhcnMgPC0gc2FwcGx5KGRhdGFzZXQyLCBpcy5jaGFyYWN0ZXIpXG5cbiMgQ29udmVydCB0aGUgaWRlbnRpZmllZCBjaGFyYWN0ZXIgdmFyaWFibGVzIGluIGRhdGFzZXQyIHRvIGZhY3RvcnNcbmRhdGFzZXQyW2NoYXJfdmFyc10gPC0gbGFwcGx5KGRhdGFzZXQyW2NoYXJfdmFyc10sIGFzLmZhY3RvcilcblxuYGBgXG5gYGAifQ== -->

```r
```r

# Read the CSV file from github
dataset2= read.csv(url(\https://raw.githubusercontent.com/SarahAlhindi/DM_project/main/Data%20Set/preprocessedDataset.csv\), header=TRUE)

# Identify the character variables in the dataset2
char_vars <- sapply(dataset2, is.character)

# Convert the identified character variables in dataset2 to factors
dataset2[char_vars] <- lapply(dataset2[char_vars], as.factor)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


## balancing data

To resolve the problem of class imbalance in the dataset, we will use SMOTE() method that oversample the minority class by creating synthetic samples using the existing minority class samples


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5iYWxhbmNlZF9kYXRhc2V0IDwtIFNNT1RFKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhc2V0MiwgcGVyYy5vdmVyID0gMzAwLCBwZXJjLnVuZGVyPTUwMCwgayA9IDEwKVxuYGBgXG5gYGAifQ== -->

```r
```r
set.seed(10)
balanced_dataset <- SMOTE(salary_in_usd ~ ., dataset2, perc.over = 300, perc.under=500, k = 10)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->



## Data mining techniques


The goal of all preceding steps is to properly prepare the dataset for the classification and clustering, which constitutes one of our primary mining objectives. In this section, we will employ various attribute selection methods such as the Gini index, Gain ratio, and information gain to construct a decision tree model. We will thoroughly evaluate its performance, and if it proves effective, it can subsequently be utilized to classify new instances with unknown class labels. The process to predict is as follow, divide the data into training and data sets then training the model using the training set and test its performance using the test set.

since our dataset is small, we decided to use K-fold Cross-validation as a dataset partioning method. for each attribute selection method we will try different K size (10,5, and 3)

in all this section we will be using train and trainControl functions of caret package to produce decision trees. for Gini index the method will be "rpart” from "rpart"  package and for Gain ratio it's "j48" from "RWeka" package as for information gain the method is "C5.0" from "C50" package  .



Data clustering is a process to partition data into groups or clusters,it is an unsupervised learning process, which is excuted without knowing the class label of the training data. The data in the same group "cluster" are similar to one another and different from data in other clusters. And for this data mining task We will utilize k-means clustering. 
We will use the method "fviz_nbclust"  of the package "factoextra" to find the number of clusters based on the elbow method and the Silhouette coefficient. To use the kmeans clustering we will utilize the method “kmeans” of the package “stats”. To visualize the clusters, we will use the method “fviz_cluster” from the package “factoextra”. And finally to find the average silhouette for each cluster the method “silhouette” from the package “cluster” will be used. 


## Evaluation and Comparison



### Classification



the following function will be used to compute average sensitivity and Specificity:


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5cbm1hY3JvID0gZnVuY3Rpb24obWF0cml4KXtcbiAgXG4gIHN1bVNlbj0wXG4gIFxuICBmb3IgKGkgaW4gMTo1KSB7XG4gICBzdW1TZW4gPSBzdW1TZW4gKyBtYXRyaXgkYnlDbGFzc1tpLDFdIFxuICB9XG4gIFxuICBcbiAgYXZnU2VuID0gc3VtU2VuLzVcbiAgXG4gIHN1bVNwZWM9MFxuICBcbiAgZm9yIChpIGluIDE6NSkge1xuICAgc3VtU3BlYyA9IHN1bVNwZWMgKyBtYXRyaXgkYnlDbGFzc1tpLDJdIFxuICB9XG4gIGF2Z1NwZWMgPSBzdW1TcGVjLzVcbiAgXG4gIFxuICBcbiAgXG4gIHN1bVByZWM9MFxuICBcbiAgZm9yIChpIGluIDE6NSkge1xuICAgc3VtUHJlYyA9IHN1bVByZWMgKyBtYXRyaXgkYnlDbGFzc1tpLDNdIFxuICB9XG4gIGF2Z1ByZWMgPSBzdW1QcmVjLzVcbiAgXG4gIFxuICBcbiAgXG4gIGF2Z3MgPSBkYXRhLmZyYW1lKFNlbnNpdGl2aXR5PWF2Z1NlbiAsIFNwZWNpZmljaXR5PWF2Z1NwZWMsIFByZWNpc2lvbj1hdmdQcmVjICxBY2N1cmFjeT0gdW5uYW1lKCBtYXRyaXgkb3ZlcmFsbFsxXSkgKVxuICBwcmludChhdmdzKVxuICBcbiAgXG59XG5cblxuYGBgXG5gYGAifQ== -->

```r
```r


macro = function(matrix){
  
  sumSen=0
  
  for (i in 1:5) {
   sumSen = sumSen + matrix$byClass[i,1] 
  }
  
  
  avgSen = sumSen/5
  
  sumSpec=0
  
  for (i in 1:5) {
   sumSpec = sumSpec + matrix$byClass[i,2] 
  }
  avgSpec = sumSpec/5
  
  
  
  
  sumPrec=0
  
  for (i in 1:5) {
   sumPrec = sumPrec + matrix$byClass[i,3] 
  }
  avgPrec = sumPrec/5
  
  
  
  
  avgs = data.frame(Sensitivity=avgSen , Specificity=avgSpec, Precision=avgPrec ,Accuracy= unname( matrix$overall[1]) )
  print(avgs)
  
  
}

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


#### Gini index

Gini index measures the impurity of the dataset. The partitioning that yields the most substantial reduction in impurity is selected as the splitting attribute. To apply the Gini index, we will employ the "rpart" method, which utilizes the Gini index as the criteria for splitting.

##### 10 Folds

The tree of the gini index using 10 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5zZXQuc2VlZCgxMClcbmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9IFxcY3ZcXCwgbnVtYmVyID0gMTAsIHJldHVyblJlc2FtcD1cXGFsbFxcLCBzYXZlUHJlZGljdGlvbnM9XFxmaW5hbFxcKVxuXG50dW5lR3JpZCA8LSBleHBhbmQuZ3JpZChjcCA9IGMoMC4wMDEsIDAuMDA1LCAwLjAxKSlcblxuZ2luaUluZGV4MTAgPC0gdHJhaW4oXG4gIHNhbGFyeV9pbl91c2QgfiAuLFxuICBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCxcbiAgbWV0aG9kID0gXFxycGFydFxcLFxuICB0ckNvbnRyb2wgPSBjdHJsLHR1bmVHcmlkPXR1bmVHcmlkLFxuICBjb250cm9sID0gbGlzdChcbiAgICBtaW5zcGxpdCA9IDEwLFxuICAgIG1pbmJ1Y2tldCA9IDUsXG4gICAgeHZhbCA9IDEwLFxuICAgIGNwID0gMC4wMDAxXG4gIClcblxuKVxuXG5cbnBycChnaW5pSW5kZXgxMCRmaW5hbE1vZGVsLCBib3gucGFsZXR0ZSA9IFxcUmVkc1xcLCB0d2VhayA9IDEuMiwgdmFybGVuID0gMjApXG5cbmBgYFxuYGBgIn0= -->

```r
```r

set.seed(10)
ctrl <- trainControl(method = \cv\, number = 10, returnResamp=\all\, savePredictions=\final\)

tuneGrid <- expand.grid(cp = c(0.001, 0.005, 0.01))

giniIndex10 <- train(
  salary_in_usd ~ .,
  data = balanced_dataset,
  method = \rpart\,
  trControl = ctrl,tuneGrid=tuneGrid,
  control = list(
    minsplit = 10,
    minbucket = 5,
    xval = 10,
    cp = 0.0001
  )

)


prp(giniIndex10$finalModel, box.palette = \Reds\, tweak = 1.2, varlen = 20)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the "experince level" attribute was selected as the first splitting attribute meaning that it has the largest impurity reduction.

###### Confusion matrix of 10 folds using Gini Index

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5naW5pSW5kZXgxMGNtID0gY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChnaW5pSW5kZXgxMCRwcmVkJG9icyxnaW5pSW5kZXgxMCRwcmVkJHByZWQpXG5cbmdpbmlJbmRleDEwY21cblxuYGBgXG5gYGAifQ== -->

```r
```r

giniIndex10cm = caret::confusionMatrix(giniIndex10$pred$obs,giniIndex10$pred$pred)

giniIndex10cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the metrics shown for each class indicate the value of that metric when treating this class as the positive class and the other classes as the negative class. here the classifier showed best performance when using the "very high" class as the positive class but this value in its own doesn't hold much value since all classes should be taken into consideration.

##### 5 Folds

The tree of the gini index using 5 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDUsIHJldHVyblJlc2FtcD1cXGFsbFxcLCBzYXZlUHJlZGljdGlvbnM9XFxmaW5hbFxcKVxuXG5cbnR1bmVHcmlkIDwtIGV4cGFuZC5ncmlkKGNwID0gYygwLjAwMSwgMC4wMDUsIDAuMDEpKVxuXG5naW5pSW5kZXg1IDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gXFxycGFydFxcLCB0ckNvbnRyb2wgPSBjdHJsLHR1bmVHcmlkPXR1bmVHcmlkLFxuICBjb250cm9sID0gbGlzdChcbiAgICBtaW5zcGxpdCA9IDEwLFxuICAgIG1pbmJ1Y2tldCA9IDUsXG4gICAgeHZhbCA9IDEwLFxuICAgIGNwID0gMC4wMDAxXG4gICkpXG5cbnBycChnaW5pSW5kZXg1JGZpbmFsTW9kZWwsIGJveC5wYWxldHRlID0gXFxSZWRzXFwsIHR3ZWFrID0gMS41LCB2YXJsZW4gPSAxMCwgY2V4ID0gMC4xNSlcblxuXG5gYGBcbmBgYCJ9 -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 5, returnResamp=\all\, savePredictions=\final\)


tuneGrid <- expand.grid(cp = c(0.001, 0.005, 0.01))

giniIndex5 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \rpart\, trControl = ctrl,tuneGrid=tuneGrid,
  control = list(
    minsplit = 10,
    minbucket = 5,
    xval = 10,
    cp = 0.0001
  ))

prp(giniIndex5$finalModel, box.palette = \Reds\, tweak = 1.5, varlen = 10, cex = 0.15)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


this tree has the same structure as the previous tree that used 10 folds. so in this tree as well "experience level" was choose as the first splitting attribute

###### Confusion matrix of 5 folds using Gini Index

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZ2luaUluZGV4NWNtID0gY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChnaW5pSW5kZXg1JHByZWQkb2JzLGdpbmlJbmRleDUkcHJlZCRwcmVkKVxuXG5naW5pSW5kZXg1Y21cblxuYGBgXG5gYGAifQ== -->

```r
```r
giniIndex5cm = caret::confusionMatrix(giniIndex5$pred$obs,giniIndex5$pred$pred)

giniIndex5cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the results are very close to the 10 folds tree, so here as well the classifier shows better performance when dealing with the "very high"

##### 3 Folds

The tree of the gini index using 3 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDMsIHJldHVyblJlc2FtcD1cXGFsbFxcLCBzYXZlUHJlZGljdGlvbnM9XFxmaW5hbFxcKVxuXG5cbnR1bmVHcmlkIDwtIGV4cGFuZC5ncmlkKGNwID0gYygwLjAwMSwgMC4wMDUsIDAuMDEpKVxuXG5naW5pSW5kZXgzIDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gXFxycGFydFxcLCB0ckNvbnRyb2wgPSBjdHJsLHR1bmVHcmlkPXR1bmVHcmlkLFxuICBjb250cm9sID0gbGlzdChcbiAgICBtaW5zcGxpdCA9IDEwLFxuICAgIG1pbmJ1Y2tldCA9IDUsXG4gICAgeHZhbCA9IDEwLFxuICAgIGNwID0gMC4wMDAxXG4gICkpXG5cbnBycChnaW5pSW5kZXgzJGZpbmFsTW9kZWwsIGJveC5wYWxldHRlID0gXFxSZWRzXFwsIHR3ZWFrID0gMS41LCB2YXJsZW4gPSAxMCwgY2V4ID0gMC4xNSlcblxuXG5gYGBcbmBgYCJ9 -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 3, returnResamp=\all\, savePredictions=\final\)


tuneGrid <- expand.grid(cp = c(0.001, 0.005, 0.01))

giniIndex3 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \rpart\, trControl = ctrl,tuneGrid=tuneGrid,
  control = list(
    minsplit = 10,
    minbucket = 5,
    xval = 10,
    cp = 0.0001
  ))

prp(giniIndex3$finalModel, box.palette = \Reds\, tweak = 1.5, varlen = 10, cex = 0.15)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


The tree shows similar structure as the two previous two trees, whether it's in its choose of the splitting attributes or the leaves.

###### Confusion matrix of 3 folds using Gini Index

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5naW5pSW5kZXgzY20gPSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGdpbmlJbmRleDMkcHJlZCRvYnMsZ2luaUluZGV4MyRwcmVkJHByZWQpXG5cbmdpbmlJbmRleDNjbVxuXG5gYGBcbmBgYCJ9 -->

```r
```r

giniIndex3cm = caret::confusionMatrix(giniIndex3$pred$obs,giniIndex3$pred$pred)

giniIndex3cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


here as well the "very high" class has the best overall performance

###### Analysis of the gini index classification

All three trees seem to be alike in their arrangement and form.

1.  Root Node - Experience Level: The initial attribute used for splitting the dataset at the root node is the "experience level." This divides the tree into two main branches or subtrees:
    -   Right Subtree: This comprises instances with Senior (SE) and Executive (EX) experience levels.
    -   Left Subtree: This includes individuals with Entry (EN) and Mid (MI) experience levels.
2.  Right Subtree - work year: The next attribute used to further classify the right subtree is "work year." The decision criterion is:
    -   If work year is \<-1.8: Then it is high.
    -   If work year is NOT \<-1.8: The next attribute examined is "experience level."
3.  Left Subtree - Experience Level: On the left side of the tree, the attribute "experience level." is used to further bifurcate the instances:
    -   If experience level is \>=2: The next attribute examined is "experience level."
    -   If experience level is NOT \>=2: The next attribute also will examined is "experience level."

###### Sensitivity, Accuracy, Specifity and precision of all 3,5 and 10 folds using Gini Index


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxucmJpbmQoXFwxMCBGb2xkc1xcPW1hY3JvKGdpbmlJbmRleDEwY20pLCBcXDUgRm9sZHNcXD1tYWNybyhnaW5pSW5kZXg1Y20pLCBcXDMgRm9sZHNcXD1tYWNybyhnaW5pSW5kZXgzY20pICApIFxuYGBgXG5gYGAifQ== -->

```r
```r
rbind(\10 Folds\=macro(giniIndex10cm), \5 Folds\=macro(giniIndex5cm), \3 Folds\=macro(giniIndex3cm)  ) 

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


The higher values for sensitivity, specificity, precision, and accuracy in the 10-fold case indicate better overall performance according to these metrics. so,Gini Index model performs better with a 10-fold cross-validation compared to 5 and 3 folds.

#### Gain ratio

The gain ratio, a normalized measure of information gain, is calculated by dividing information gain by the split information. The attribute that yields the highest gain ratio is chosen as the splitting attribute. The C4.5 algorithm employs the gain ratio.

The J48 is the Java-based open-source implementation of the C4.5 algorithm, and it is included in the Weka package. This implementation allows users to conveniently apply the C4.5 decision tree.

##### 10 Folds

The tree of the gain ratio using 10 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDEwLCByZXR1cm5SZXNhbXA9XFxhbGxcXCwgc2F2ZVByZWRpY3Rpb25zPVxcZmluYWxcXClcbmdhaW5SYXRpbzEwIDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gXFxKNDhcXCx0ckNvbnRyb2wgPSBjdHJsKVxucGxvdChnYWluUmF0aW8xMCRmaW5hbE1vZGVsKVxuYGBgXG5gYGAifQ== -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 10, returnResamp=\all\, savePredictions=\final\)
gainRatio10 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \J48\,trControl = ctrl)
plot(gainRatio10$finalModel)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the first splitting attribute that was choosen is the "Expeirence level" attribute meaning that it probably has a high information gain and low splitInfo(Entropy of distribution of tuples into partition)

###### Confusion matrix of 10 folds using Gain ratio

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZ2FpblJhdGlvMTBjbSA9IGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZ2FpblJhdGlvMTAkcHJlZCRvYnMsIGdhaW5SYXRpbzEwJHByZWQkcHJlZClcblxuZ2FpblJhdGlvMTBjbVxuXG5cbmBgYFxuYGBgIn0= -->

```r
```r
gainRatio10cm = caret::confusionMatrix(gainRatio10$pred$obs, gainRatio10$pred$pred)

gainRatio10cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


here the classifier shows better performance when treating "very high" and "very low" attributes as positive class. since the "very high" class is better in Sensitivity and "very low" is better in Specificity and precision (Pos Pred Value)

##### 5 Folds

The tree of the gain ratio using 5 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDUsIHJldHVyblJlc2FtcD1cXGFsbFxcLCBzYXZlUHJlZGljdGlvbnM9XFxmaW5hbFxcKVxuZ2FpblJhdGlvNSA8LSB0cmFpbihzYWxhcnlfaW5fdXNkIH4gLiwgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsIG1ldGhvZCA9IFxcSjQ4XFwsdHJDb250cm9sID0gY3RybClcbnBsb3QoZ2FpblJhdGlvNSRmaW5hbE1vZGVsKVxuYGBgXG5gYGAifQ== -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 5, returnResamp=\all\, savePredictions=\final\)
gainRatio5 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \J48\,trControl = ctrl)
plot(gainRatio5$finalModel)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the tree is similar to the tree that was resulted from 10 folds. it has choose "Experience level" as the first splitting attribute and and seem to show similar behavior.

###### Confusion matrix of 5 folds using Gain ratio

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5nYWluUmF0aW81Y209Y2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChnYWluUmF0aW81JHByZWQkb2JzLCBnYWluUmF0aW81JHByZWQkcHJlZClcblxuZ2FpblJhdGlvNWNtXG5cbmBgYFxuYGBgIn0= -->

```r
```r

gainRatio5cm=caret::confusionMatrix(gainRatio5$pred$obs, gainRatio5$pred$pred)

gainRatio5cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


unlike the 10 folds, here the classifier has the best overall performance when considering the "very high" as the positive class.

##### 3 Folds

The tree of the gain ratio using 3 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDMsIHJldHVyblJlc2FtcD1cXGFsbFxcLCBzYXZlUHJlZGljdGlvbnM9XFxmaW5hbFxcKVxuZ2FpblJhdGlvMyA8LSB0cmFpbihzYWxhcnlfaW5fdXNkIH4gLiwgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsIG1ldGhvZCA9IFxcSjQ4XFwsdHJDb250cm9sID0gY3RybClcbnBsb3QoZ2FpblJhdGlvMyRmaW5hbE1vZGVsKVxuYGBgXG5gYGAifQ== -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 3, returnResamp=\all\, savePredictions=\final\)
gainRatio3 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \J48\,trControl = ctrl)
plot(gainRatio3$finalModel)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the tree shows similar behavior as the previous 2 trees that resulted from using 10 and 5 folds.

###### Confusion matrix of 3 folds using Gain ratio

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZ2FpblJhdGlvM2NtPWNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZ2FpblJhdGlvMyRwcmVkJG9icywgZ2FpblJhdGlvMyRwcmVkJHByZWQpXG5cbmdhaW5SYXRpbzNjbVxuXG5gYGBcbmBgYCJ9 -->

```r
```r
gainRatio3cm=caret::confusionMatrix(gainRatio3$pred$obs, gainRatio3$pred$pred)

gainRatio3cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


similar to teh 5 folds, the "very high" class has the best metrics.

###### Analysis of the gain ratio classification

The observed structure of the three decision trees seems to be the same and it can be summarized as follows:

1.  Root Node - Experience Level: The initial attribute used for splitting the dataset at the root node is the "experience level." This divides the tree into two main branches or subtrees:
    -   Right Subtree: This comprises instances with Senior (SE) and Executive (EX) experience levels.
    -   Left Subtree: This includes individuals with Entry (EN) and Mid (MI) experience levels.
2.  Within the right subtree:
    -   If the 'experience level' is 4 (EX, Executive level) , the tree splits based on the 'Employee_residence' attribute. It checks whether the 'Employee_residence' is 'Latin America.'
    -   If 'Employee_residence' does not equal 'Latin America,' the differentiation continues with the 'remote_ratio' attribute, further dividing the tree.
3.  Within the left subtree:
    -   If the 'experience level' is 1 (EN, Entry level), the tree divide based on the 'Employee_residence' attribute, specifically checking for 'Sub-Saharan Africa.'
    -   If the 'experience level' is 2 (MI, Mid level), it also branches based on 'Employee_residence,' but in this case, looking to see if it equals 'North America.'

The decision tree continues to select the most appropriate attributes for splitting at each node, progressively refining the decision process until it reaches the leaves, where final class labels are assigned to the instances.

###### Sensitivity, Accuracy, Specifity and precision of all 3,5 and 10 folds using Gain ratio


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxucmJpbmQoXFwxMCBGb2xkc1xcPW1hY3JvKGdhaW5SYXRpbzEwY20pLCBcXDUgRm9sZHNcXD1tYWNybyhnYWluUmF0aW81Y20pLCBcXDMgRm9sZHNcXD1tYWNybyhnYWluUmF0aW8zY20pICApIFxuYGBgXG5gYGAifQ== -->

```r
```r
rbind(\10 Folds\=macro(gainRatio10cm), \5 Folds\=macro(gainRatio5cm), \3 Folds\=macro(gainRatio3cm)  ) 

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


Based on the evaluation metrics of average Sensitivity,Precision ,Specificity, and Accuracy, it is evident that the gain ratio model, built using a 10-fold cross-validation approach, exhibits superior performance compared to the other two models. However, it's worth noting that the difference in performance between the models is relatively small.

A detailed examination of the results from the 10-fold cross-validation reveals that the model has a notably high specificity compared to other metrics. This high specificity suggests that the model is particularly effective at correctly identifying instances that do not pertain to the target class---essentially, it accurately recognizes when examples are not members of the specified class. For example, if the positive class in question is "High" then the model is able to correctly classify tuples that belong to "Very Low", "Medium", and "Very High".

However, possessing high specificity alone does not guarantee the overall effectiveness of the model, as a well-rounded model also requires balanced performance across other metrics. In this case, its ability to capture and classify instances that do belong to the positive class (as measured by sensitivity) is not as robust. For a model to be considered truly effective, it would need to demonstrate strong performance in all metrics specificity and sensitivity, ensuring it can accurately distinguish both negative and positive instances as well as accuracy precision.

#### Information gain

Information Gain is a metric used to decide which attribute to choose for splitting the data at each node in the decision tree. For a given dataset, the Information Gain of an attribute is calculated by comparing the entropy before and after the dataset is split based on that attribute. The attribute with the highest Information Gain is chosen as the splitting attribute.

##### 10 Folds

The tree of the information gain using 10 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDEwLCByZXR1cm5SZXNhbXA9XFxhbGxcXCwgc2F2ZVByZWRpY3Rpb25zPVxcZmluYWxcXClcblxuXG5pbmZvR2FpbjEwIDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gXFxDNS4wXFwsdHJDb250cm9sID0gY3RybClcblxuYzVtb2RlbCA8LSBDNS4wKHNhbGFyeV9pbl91c2QgfiAuLFxuICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCxcbiAgICAgICAgICAgICAgICAgICAgICAgdHJpYWxzID0gaW5mb0dhaW4xMCRiZXN0VHVuZSR0cmlhbHMsIFxuICAgICAgICAgICAgICAgICAgICAgICBydWxlcyA9IEZBTFNFLFxuICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gQzUuMENvbnRyb2wod2lubm93ID0gaW5mb0dhaW4xMCRiZXN0VHVuZSR3aW5ub3cpKVxucGxvdChjNW1vZGVsKVxuYGBgXG5gYGAifQ== -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 10, returnResamp=\all\, savePredictions=\final\)


infoGain10 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \C5.0\,trControl = ctrl)

c5model <- C5.0(salary_in_usd ~ .,
                       data = balanced_dataset,
                       trials = infoGain10$bestTune$trials, 
                       rules = FALSE,
                       control = C5.0Control(winnow = infoGain10$bestTune$winnow))
plot(c5model)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


from the tree, the "experince level" attribute was the first selected splitting attribute meaning that it has the highest information gain among all attributes

###### Confusion matrix of 10 folds using Information gain

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuaW5mb0dhaW4xMGNtPSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGluZm9HYWluMTAkcHJlZCRvYnMsIGluZm9HYWluMTAkcHJlZCRwcmVkKVxuXG5pbmZvR2FpbjEwY21cblxuYGBgXG5gYGAifQ== -->

```r
```r
infoGain10cm= caret::confusionMatrix(infoGain10$pred$obs, infoGain10$pred$pred)

infoGain10cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


similar to the trees from the gini index and gain ratio, the classifier seem to have better performance when treating the "very high" class as the positive class

##### 5 Folds

The tree of the information gain using 5 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDUsIHJldHVyblJlc2FtcD1cXGFsbFxcLCBzYXZlUHJlZGljdGlvbnM9XFxmaW5hbFxcKVxuXG5cbmluZm9HYWluNSA8LSB0cmFpbihzYWxhcnlfaW5fdXNkIH4gLiwgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsIG1ldGhvZCA9IFxcQzUuMFxcLHRyQ29udHJvbCA9IGN0cmwpXG5cbmM1bW9kZWwgPC0gQzUuMChzYWxhcnlfaW5fdXNkIH4gLixcbiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsXG4gICAgICAgICAgICAgICAgICAgICAgIHRyaWFscyA9IGluZm9HYWluNSRiZXN0VHVuZSR0cmlhbHMsIFxuICAgICAgICAgICAgICAgICAgICAgICBydWxlcyA9IEZBTFNFLFxuICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gQzUuMENvbnRyb2wod2lubm93ID0gaW5mb0dhaW41JGJlc3RUdW5lJHdpbm5vdykpXG5wbG90KGM1bW9kZWwpXG5gYGBcbmBgYCJ9 -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 5, returnResamp=\all\, savePredictions=\final\)


infoGain5 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \C5.0\,trControl = ctrl)

c5model <- C5.0(salary_in_usd ~ .,
                       data = balanced_dataset,
                       trials = infoGain5$bestTune$trials, 
                       rules = FALSE,
                       control = C5.0Control(winnow = infoGain5$bestTune$winnow))
plot(c5model)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the tree has similar behavior as the 10 folds information gain tree

###### Confusion matrix of 5 folds using Information gain

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuaW5mb0dhaW41Y20gPSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGluZm9HYWluNSRwcmVkJG9icywgaW5mb0dhaW41JHByZWQkcHJlZClcblxuaW5mb0dhaW41Y21cblxuYGBgXG5gYGAifQ== -->

```r
```r
infoGain5cm = caret::confusionMatrix(infoGain5$pred$obs, infoGain5$pred$pred)

infoGain5cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the classifier shows very close performance to the 10 folds information gain model

##### 3 Folds

The tree of the information gain using 3 folds


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuc2V0LnNlZWQoMTApXG5jdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSBcXGN2XFwsIG51bWJlciA9IDMsIHJldHVyblJlc2FtcD1cXGFsbFxcLCBzYXZlUHJlZGljdGlvbnM9XFxmaW5hbFxcKVxuXG5cbmluZm9HYWluMyA8LSB0cmFpbihzYWxhcnlfaW5fdXNkIH4gLiwgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsIG1ldGhvZCA9IFxcQzUuMFxcLHRyQ29udHJvbCA9IGN0cmwpXG5cbmM1bW9kZWwgPC0gQzUuMChzYWxhcnlfaW5fdXNkIH4gLixcbiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsXG4gICAgICAgICAgICAgICAgICAgICAgIHRyaWFscyA9IGluZm9HYWluMyRiZXN0VHVuZSR0cmlhbHMsIFxuICAgICAgICAgICAgICAgICAgICAgICBydWxlcyA9IEZBTFNFLFxuICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gQzUuMENvbnRyb2wod2lubm93ID0gaW5mb0dhaW4zJGJlc3RUdW5lJHdpbm5vdykpXG5wbG90KGM1bW9kZWwpXG5gYGBcbmBgYCJ9 -->

```r
```r
set.seed(10)
ctrl <- trainControl(method = \cv\, number = 3, returnResamp=\all\, savePredictions=\final\)


infoGain3 <- train(salary_in_usd ~ ., data = balanced_dataset, method = \C5.0\,trControl = ctrl)

c5model <- C5.0(salary_in_usd ~ .,
                       data = balanced_dataset,
                       trials = infoGain3$bestTune$trials, 
                       rules = FALSE,
                       control = C5.0Control(winnow = infoGain3$bestTune$winnow))
plot(c5model)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


the visisble parts of the tree seem to behave the same as the prevoius 2 fold sizes- 10 and 5.

###### Confusion matrix of 3 folds using Information gain

The following confusion Matrix will show the performance of the classifier using the predicted class labels and the actual class labels of our dataset


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuaW5mb0dhaW4zY20gPSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGluZm9HYWluMyRwcmVkJG9icywgaW5mb0dhaW4zJHByZWQkcHJlZClcblxuaW5mb0dhaW4zY21cblxuYGBgXG5gYGAifQ== -->

```r
```r
infoGain3cm = caret::confusionMatrix(infoGain3$pred$obs, infoGain3$pred$pred)

infoGain3cm

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


since the tree is essentially similar to the previous two information gain trees the results that this tree shows is very close in performance to them as well.

###### Analysis of the information gain classification

The observed structure of the three decision trees seems to be the same and it can be summarized as follows:

1.  Root Node - Experience Level: The initial attribute used for splitting the dataset at the root node is the "experience level." This divides the tree into two main branches or subtrees:

    -   Right Subtree: This comprises instances with Senior (SE) and Executive (EX) experience levels.
    -   Left Subtree: This includes individuals with Entry (EN) and Mid (MI) experience levels.

2.  Within the right subtree: In the right sub tree if the experience level is 4(EX) the tree will be divided based on "Company location"

3.  Within the left subtree: In the left subtree it will divide the tree for both two experience levels 1(EN) and 2(MI) based on "employee residence" and when the "employee residence" is "North America" the tree will be further divided based on "salary currency" and when this attribute is equal to "USD" the division will be based on the "job title" attribute

The decision tree continues to select the most appropriate attributes for splitting at each node, progressively refining the decision process until it reaches the leaves, where final class labels are assigned to the instances.

###### Sensitivity, Accuracy, Specifity and precision of all 3,5 and 10 folds using Information gain


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxucmJpbmQoXFwxMCBGb2xkc1xcPW1hY3JvKGluZm9HYWluMTBjbSksIFxcNSBGb2xkc1xcPW1hY3JvKGluZm9HYWluNWNtKSwgXFwzIEZvbGRzXFw9bWFjcm8oaW5mb0dhaW4zY20pICApIFxuYGBgXG5gYGAifQ== -->

```r
```r
rbind(\10 Folds\=macro(infoGain10cm), \5 Folds\=macro(infoGain5cm), \3 Folds\=macro(infoGain3cm)  ) 

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


Based on the provided sensitivity, specificity, precision, and accuracy values there isn't a clear indication of the superiority of one fold over another for Information Gain model .we may need to consider additional factors or conduct further analysis to make a well-informed decision. as can be seen in the table the 10 folds has the best Specificity and Precision, meanwhile the 5 folds has the best Sensitivity and Accuracy.

### Clustering

Data clustering is a process to partition data into groups or clusters,it is an unsupervised learning process, which is excuted without knowing the class label of the training data. The data in the same group "cluster" are similar to one another and different from data in other clusters. And for this data mining task We will utilize k-means clustering.

#### 1- prepreocessing

we will encode the rest of factor columns to transform them into numeric types before clustering, enabling meaningful distance calculations using kmeans and other formulas, and allowing for maximum flexibility in data processing and interpretation. we will also remove the class label from the dataset as clustering is an unsupervised learning process, and we will preserve this class label in an attribute for later use. lastly, we will scale all numeric attributes in the dataset so they will be standarized.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG4jIHZpZXcgZGF0YVxuXG5kYXRhc2V0MyA8LSBkYXRhc2V0MlxuVmlldyhkYXRhc2V0MylcblxuIyBSZXNlcnZlIHRoZSBzYWxhcnlfaW5fdXNkICh0aGUgY2xhc3MgbGFiZWwpIGNvbHVtbiBpbiBhbiBhdHRyaWJ1dGUgYmVmb3JlIHJlbW92aW5nIGl0IGZyb20gdGhlIGRhdGFzZXQgZm9yIGNsdXN0ZXJpbmdcblxuY2xhc3NMYWJlbCA8LSBkYXRhc2V0M1ssIDVdIFxuXG5cbiMgUmVtb3ZlIHRoZSBjbGFzcyBsYWJsZSBmcm9tIHRoZSBkYXRhc2V0XG5cbmRhdGFzZXQzIDwtIGRhdGFzZXQzWywgLTVdXG5cbiMgZW5jb2Rpbmcgam9iX3RpdGxlIHZhcmlhYmxlXG5cbmRhdGFzZXQzJGpvYl90aXRsZSA9IGZhY3RvcihkYXRhc2V0MyRqb2JfdGl0bGUsIGxldmVscz1jKFxcQW5hbHlzdFxcLCBcXEFyY2hpdGVjdFxcLCBcXEVuZ2luZWVyXFwsIFxcTGVhZGVyc2hpcFxcLCBcXENvbnN1bHRhbnQvU3BlY2lhbGlzdFxcLFxcQ3liZXIgU2VjdXJpdHlcXCxcXE90aGVyc1xcICksIGxhYmVscz1jKDQsMSwyLDUsMyw2LDcpKVxuXG4jIGVuY29kaW5nIHNhbGFyeV9jdXJyZW5jeSB2YXJpYWJsZVxuXG5kYXRhc2V0MyRzYWxhcnlfY3VycmVuY3kgPSBmYWN0b3IoZGF0YXNldDMkc2FsYXJ5X2N1cnJlbmN5LCBsZXZlbHM9YyhcXFVTRFxcLFxcQlJMXFwsXFxHQlBcXCxcXEVVUlxcLFxcSU5SXFwsXFxDQURcXCxcXENIRlxcLFxcREtLXFwsXFxTR0RcXCxcXEFVRFxcLFxcU0VLXFwsXFxNWE5cXCxcXElMU1xcLFxcUExOXFwsXFxOT0tcXCxcXElEUlxcLFxcTlpEXFwsXFxIVUZcXCxcXFpBUlxcLFxcVFdEXFwsXFxSVUJcXCksIGxhYmVscz1jKDEsMiwzLDQsNSw2LDcsOCw5LDEwLDExLDEyLDEzLDE0LDE1LDE2LDE3LDE4LDE5LDIwLDIxKSlcblxuIyBlbmNvZGluZyBlbXBsb3llZV9yZXNpZGVuY2UgdmFyaWFibGVcblxuZGF0YXNldDMkZW1wbG95ZWVfcmVzaWRlbmNlID0gZmFjdG9yKGRhdGFzZXQzJGVtcGxveWVlX3Jlc2lkZW5jZSwgbGV2ZWxzPWMoXFxOb3J0aCBBbWVyaWNhXFwsXFxMYXRpbiBBbWVyaWNhICYgQ2FyaWJiZWFuXFwsXFxTdWItU2FoYXJhbiBBZnJpY2FcXCwgXFxFdXJvcGUgJiBDZW50cmFsIEFzaWFcXCxcXEVhc3QgQXNpYSAmIFBhY2lmaWNcXCxcXFNvdXRoIEFzaWFcXCxcXE1pZGRsZSBFYXN0ICYgTm9ydGggQWZyaWNhXFwpLCBsYWJlbHM9YygxLDIsMyw0LDUsNiw3KSlcblxuIyBlbmNvZGluZyBjb21wYW55X2xvY2F0aW9uIHZhcmlhYmxlXG5cbmRhdGFzZXQzJGNvbXBhbnlfbG9jYXRpb24gPSBmYWN0b3IoZGF0YXNldDMkY29tcGFueV9sb2NhdGlvbiwgbGV2ZWxzPWMoXFxOb3J0aCBBbWVyaWNhXFwsXFxMYXRpbiBBbWVyaWNhICYgQ2FyaWJiZWFuXFwsXFxTdWItU2FoYXJhbiBBZnJpY2FcXCwgXFxFdXJvcGUgJiBDZW50cmFsIEFzaWFcXCxcXEVhc3QgQXNpYSAmIFBhY2lmaWNcXCxcXFNvdXRoIEFzaWFcXCxcXE1pZGRsZSBFYXN0ICYgTm9ydGggQWZyaWNhXFwsIFxcQVFcXCwgXFxVTVxcKSwgbGFiZWxzPWMoMSwyLDMsNCw1LDYsNyw4LDkpKVxuXG5cbiBcbiNEYXRhIHR5cGVzIHRvIGJlIHRyYW5zZm9ybWVkIGludG8gbnVtZXJpYyB0eXBlcyBiZWZvcmUgY2x1c3RlcmluZ1xuI1RyYW5zZm9ybWluZyBhbGwgbm9uLW51bWVyaWMgYXR0cmlidXRlcyB0byBudW1lcmljIHR5cGVcblxuXG5kYXRhc2V0MyRleHBlcmllbmNlX2xldmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFzZXQzJGV4cGVyaWVuY2VfbGV2ZWwpKVxuXG5kYXRhc2V0MyRqb2JfdGl0bGUgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YXNldDMkam9iX3RpdGxlKSlcblxuZGF0YXNldDMkc2FsYXJ5X2N1cnJlbmN5IDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFzZXQzJHNhbGFyeV9jdXJyZW5jeSkpXG5cbmRhdGFzZXQzJGVtcGxveWVlX3Jlc2lkZW5jZSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhc2V0MyRlbXBsb3llZV9yZXNpZGVuY2UpKVxuXG5kYXRhc2V0MyRjb21wYW55X2xvY2F0aW9uIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFzZXQzJGNvbXBhbnlfbG9jYXRpb24pKVxuXG5kYXRhc2V0MyRjb21wYW55X3NpemUgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YXNldDMkY29tcGFueV9zaXplKSlcblxuIyB2aXdlIHRoZSBjbGFzcyBvZiBhdHRyaWJ1dGVzIHRvIGVuc3VyZSB0aGV5IGhhdmUgdHJhbnNmb3JtZWQgdG8gbnVtZXJpY1xuc2FwcGx5KGRhdGFzZXQzLCBjbGFzcylcblxuXG4jc2NhbGUgYWxsIGF0dHJpYnV0ZXMgaW4gdGhlIGRhdGFzZXQgc28gdGhleSB3b3VsZCBiZSBzdGFuZGFyZGl6ZWQgXG5kYXRhc2V0MyA8LSBzY2FsZShkYXRhc2V0MylcblxuYGBgXG5gYGAifQ== -->

```r
```r

# view data

dataset3 <- dataset2
View(dataset3)

# Reserve the salary_in_usd (the class label) column in an attribute before removing it from the dataset for clustering

classLabel <- dataset3[, 5] 


# Remove the class lable from the dataset

dataset3 <- dataset3[, -5]

# encoding job_title variable

dataset3$job_title = factor(dataset3$job_title, levels=c(\Analyst\, \Architect\, \Engineer\, \Leadership\, \Consultant/Specialist\,\Cyber Security\,\Others\ ), labels=c(4,1,2,5,3,6,7))

# encoding salary_currency variable

dataset3$salary_currency = factor(dataset3$salary_currency, levels=c(\USD\,\BRL\,\GBP\,\EUR\,\INR\,\CAD\,\CHF\,\DKK\,\SGD\,\AUD\,\SEK\,\MXN\,\ILS\,\PLN\,\NOK\,\IDR\,\NZD\,\HUF\,\ZAR\,\TWD\,\RUB\), labels=c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21))

# encoding employee_residence variable

dataset3$employee_residence = factor(dataset3$employee_residence, levels=c(\North America\,\Latin America & Caribbean\,\Sub-Saharan Africa\, \Europe & Central Asia\,\East Asia & Pacific\,\South Asia\,\Middle East & North Africa\), labels=c(1,2,3,4,5,6,7))

# encoding company_location variable

dataset3$company_location = factor(dataset3$company_location, levels=c(\North America\,\Latin America & Caribbean\,\Sub-Saharan Africa\, \Europe & Central Asia\,\East Asia & Pacific\,\South Asia\,\Middle East & North Africa\, \AQ\, \UM\), labels=c(1,2,3,4,5,6,7,8,9))


 
#Data types to be transformed into numeric types before clustering
#Transforming all non-numeric attributes to numeric type


dataset3$experience_level <- as.numeric(as.character(dataset3$experience_level))

dataset3$job_title <- as.numeric(as.character(dataset3$job_title))

dataset3$salary_currency <- as.numeric(as.character(dataset3$salary_currency))

dataset3$employee_residence <- as.numeric(as.character(dataset3$employee_residence))

dataset3$company_location <- as.numeric(as.character(dataset3$company_location))

dataset3$company_size <- as.numeric(as.character(dataset3$company_size))

# viwe the class of attributes to ensure they have transformed to numeric
sapply(dataset3, class)


#scale all attributes in the dataset so they would be standardized 
dataset3 <- scale(dataset3)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


#### 2- K-means

After preprocessing the data, now we are ready to perform the clustering process, we will use the k-means clustering, it is a clustering method that aims to minimize the sum of squared distances between each data point and the centroid of its assigned cluster by iteratively updating cluster assignments and centroids.

#### 3- Choosing number of clusters (k)

We will choose 3 different numbers to perform the k-means clustering on, one of the numbers should be relatevily large, the second should be in the middle and the last should be small. This way we will cover the possible outcomes and clustering results.

##### a- Silhouette method

Now we will apply Silhouette method to find the optimal number of clusters k, we will also plot a graph where x-axis represent the number of clusters and y-axis represent the average Silhouette coefficient


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5mdml6X25iY2x1c3QoZGF0YXNldDMsIGttZWFucywgbWV0aG9kID0gXFxzaWxob3VldHRlXFwpK1xuICBsYWJzKHN1YnRpdGxlID0gXFxTaWxob3VldHRlIG1ldGhvZFxcKVxuXG5gYGBcbmBgYCJ9 -->

```r
```r

fviz_nbclust(dataset3, kmeans, method = \silhouette\)+
  labs(subtitle = \Silhouette method\)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


as seen by the graph, the number of clusters k that maximizes the average Silhouette coefficient is 2, so we will use it for clustering.

##### b- Elbow method

This method determines the number of clusters according to the turning point in a curve, the curve is plotted using the total within-cluster sum of square (WSS) as in y-axis , and No. clusters in x-axis


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG5mdml6X25iY2x1c3QoZGF0YXNldDMsIGttZWFucywgbWV0aG9kID0gXFx3c3NcXCkgK1xuICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBsaW5ldHlwZSA9IDIpK1xuICBsYWJzKHN1YnRpdGxlID0gXFxFbGJvdyBtZXRob2RcXClcblxuYGBgXG5gYGAifQ== -->

```r
```r

fviz_nbclust(dataset3, kmeans, method = \wss\) +
  geom_vline(xintercept = 4, linetype = 2)+
  labs(subtitle = \Elbow method\)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


As shown, the number of clusters k that represents the turning point in the curve is 4, so we will use it for clustering.

Lastly, we will use k=3 since it acheives the second highest average Silhouette coefficient, and since it's in the middle between 2 and 4 it will strike a balance between having too few clusters (k=2), and having several clusters (k=4), Thus, this choice will have an acceptable acuuracy.

#### k-means clustering, visualization and evaluation

In this section, we will perform k-means clustering and visualize its result using three different k's that have been chosen beforehand, then we will compute WSS and Bcubed preceision and recall and average silhouette for each cluster as methods of evaluating clustering results.

##### k=2


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG4jVXNlIHNlZWQgdG8gZ3VhcmFudGVlIHJlcGxpY2FiaWxpdHkgb2YgcmFuZG9tIHByb2Nlc3Nlc1xuc2V0LnNlZWQoODk1MylcblxuIyBydW4gay1tZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgMiBjbHVzdGVyc1xua21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YXNldDMsIDIpXG5cbiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHRcbmttZWFucy5yZXN1bHRcblxuIyB2aXN1YWxpemUgY2x1c3RlcmluZ1xuZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhc2V0MylcblxuXG4jYXZlcmFnZSBzaWxob3VldHRlIGZvciBlYWNoIGNsdXN0ZXJzXG5hdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLGRpc3QoZGF0YXNldDMpKSBcbmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKVxuXG4jV2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgd3NzIFxud3NzIDwtIGttZWFucy5yZXN1bHQkdG90LndpdGhpbnNzXG5wcmludCh3c3MpXG5cbiNCQ3ViZWRcbmttZWFuc19jbHVzdGVyIDwtIGMoa21lYW5zLnJlc3VsdCRjbHVzdGVyKVxuXG5ncm91bmRfdHJ1dGggPC0gYyhjbGFzc0xhYmVsKVxuXG5kYXRhIDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGttZWFuc19jbHVzdGVyLCBsYWJlbCA9IGdyb3VuZF90cnV0aClcblxuXG4gIGJjdWJlZCA8LSBmdW5jdGlvbihkYXRhKSB7XG4gIG4gPC0gbnJvdyhkYXRhKVxuICB0b3RhbF9wcmVjZXNpb24gPC0gMFxuICB0b3RhbF9yZWNhbGwgPC0gMFxuXG5mb3IgKGkgaW4gMTpuKSB7XG4gIGNsdXN0ZXIgPC0gZGF0YSRjbHVzdGVyW2ldXG4gIGxhYmVsIDwtIGRhdGEkbGFiZWxbaV1cbiAgICBcbiMgTnVtYmVyIG9mIG9iamVjdHMgaW4gdGhlIHNhbWUgY2F0ZWdvcnkgYW5kIGNsdXN0ZXJcbmludGVyc2VjdGlvbiA8LSBzdW0oZGF0YSRsYWJlbFtkYXRhJGNsdXN0ZXIgPT0gY2x1c3Rlcl0gPT0gbGFiZWwpXG4gICAgXG4jIE51bWJlciBvZiBvYmplY3RzIHRoYXQgYXJlIGluIHRoZSBzYW1lIGNsdXN0ZXJcbnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpXG4gICAgXG4jIE51bWJlciBvZiBvYmplY3RzIHRoYXQgaGF2ZSB0aGUgc2FtZSBjYXRlZ29yeVxudG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbClcbiAgICBcblxudG90YWxfcHJlY2VzaW9uIDwtIHRvdGFsX3ByZWNlc2lvbiArIGludGVyc2VjdGlvbiAvdG90YWxfc2FtZV9jbHVzdGVyXG50b3RhbF9yZWNhbGwgPC0gdG90YWxfcmVjYWxsICsgaW50ZXJzZWN0aW9uIC8gdG90YWxfc2FtZV9jYXRlZ29yeVxuICB9XG5cbiAgIyBjb21wdXRlIGF2ZyBwcmVjaXNpb24gYW5kIHJlY2FsbFxuICBwcmVjaXNpb24gPC0gdG90YWxfcHJlY2VzaW9uIC8gblxuICByZWNhbGwgPC0gdG90YWxfcmVjYWxsIC8gblxuXG4gIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSkgfVxuXG5cbiMgY29tcHV0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGxcbm1ldHJpY3MgPC0gYmN1YmVkKGRhdGEpXG5cblxucHJlY2lzaW9uIDwtIG1ldHJpY3MkcHJlY2lzaW9uXG5yZWNhbGwgPC0gbWV0cmljcyRyZWNhbGxcblxuIyBQcmludCByZXN1bHRzXG5jYXQoXFxCQ3ViZWQgUHJlY2lzaW9uIGlzOlxcLCBwcmVjaXNpb24sIFxcXFxuXFwpXG5jYXQoXFxCQ3ViZWQgUmVjYWxsIGlzOlxcLCByZWNhbGwsIFxcXFxuXFwpXG5gYGBcbmBgYCJ9 -->

```r
```r

#Use seed to guarantee replicability of random processes
set.seed(8953)

# run k-means clustering to find 2 clusters
kmeans.result <- kmeans(dataset3, 2)

# print the clusterng result
kmeans.result

# visualize clustering
fviz_cluster(kmeans.result, data = dataset3)


#average silhouette for each clusters
avg_sil <- silhouette(kmeans.result$cluster,dist(dataset3)) 
fviz_silhouette(avg_sil)

#Within-cluster sum of squares wss 
wss <- kmeans.result$tot.withinss
print(wss)

#BCubed
kmeans_cluster <- c(kmeans.result$cluster)

ground_truth <- c(classLabel)

data <- data.frame(cluster = kmeans_cluster, label = ground_truth)


  bcubed <- function(data) {
  n <- nrow(data)
  total_precesion <- 0
  total_recall <- 0

for (i in 1:n) {
  cluster <- data$cluster[i]
  label <- data$label[i]
    
# Number of objects in the same category and cluster
intersection <- sum(data$label[data$cluster == cluster] == label)
    
# Number of objects that are in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Number of objects that have the same category
total_same_category <- sum(data$label == label)
    

total_precesion <- total_precesion + intersection /total_same_cluster
total_recall <- total_recall + intersection / total_same_category
  }

  # compute avg precision and recall
  precision <- total_precesion / n
  recall <- total_recall / n

  return(list(precision = precision, recall = recall)) }


# compute BCubed precision and recall
metrics <- bcubed(data)


precision <- metrics$precision
recall <- metrics$recall

# Print results
cat(\BCubed Precision is:\, precision, \\n\)
cat(\BCubed Recall is:\, recall, \\n\)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


we can conclude from the graph and the results that the k=2 is the optimal k, since there is no overlapping between the two clusters, the data in a cluster are close "similar" to each other and dissimilar to data in the other cluster. Also, the recall is relatively high (0.71) and is the highest among the k's chosen, the Precision is low (0.28) which could be duo to presence of outliers or sensitivity to Initial Centroid. We can also note that the WSS is 7287.657, indicating a good compactness of clusters, and that objects in a cluster are similar to one another noting that the higher the k, the lower the WSS. Lastly, the average silhouette width is 0.34 which is considered high reflecting high intra-cluster similarity.

##### k=3


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuXG4jVXNlIHNlZWQgdG8gZ3VhcmFudGVlIHJlcGxpY2FiaWxpdHkgb2YgcmFuZG9tIHByb2Nlc3Nlc1xuc2V0LnNlZWQoODk1MylcblxuIyBydW4gay1tZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgMyBjbHVzdGVyc1xua21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YXNldDMsIDMpXG5cbiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHRcbmttZWFucy5yZXN1bHRcblxuIyB2aXN1YWxpemUgY2x1c3RlcmluZ1xuZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhc2V0MylcblxuI2F2ZXJhZ2Ugc2lsaG91ZXR0ZSBmb3IgZWFjaCBjbHVzdGVyc1xuYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlcixkaXN0KGRhdGFzZXQzKSkgXG5mdml6X3NpbGhvdWV0dGUoYXZnX3NpbClcblxuI1dpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIHdzcyBcbndzcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zc1xucHJpbnQod3NzKVxuXG4jQkN1YmVkIFxua21lYW5zX2NsdXN0ZXIgPC0gYyhrbWVhbnMucmVzdWx0JGNsdXN0ZXIpXG5cbmdyb3VuZF90cnV0aCA8LSBjKGNsYXNzTGFiZWwpXG5cbmRhdGEgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0ga21lYW5zX2NsdXN0ZXIsIGxhYmVsID0gZ3JvdW5kX3RydXRoKVxuXG4gIGJjdWJlZCA8LSBmdW5jdGlvbihkYXRhKSB7XG4gIG4gPC0gbnJvdyhkYXRhKVxuICB0b3RhbF9wcmVjZXNpb24gPC0gMFxuICB0b3RhbF9yZWNhbGwgPC0gMFxuXG4gIGZvciAoaSBpbiAxOm4pIHtcbiAgICBjbHVzdGVyIDwtIGRhdGEkY2x1c3RlcltpXVxuICAgIGxhYmVsIDwtIGRhdGEkbGFiZWxbaV1cbiAgICBcbiMgTnVtYmVyIG9mIG9iamVjdHMgaW4gdGhlIHNhbWUgY2F0ZWdvcnkgYW5kIGNsdXN0ZXJcbmludGVyc2VjdGlvbiA8LSBzdW0oZGF0YSRsYWJlbFtkYXRhJGNsdXN0ZXIgPT0gY2x1c3Rlcl0gPT0gbGFiZWwpXG4gICAgXG4jIE51bWJlciBvZiBvYmplY3RzIHRoYXQgYXJlIGluIHRoZSBzYW1lIGNsdXN0ZXJcbnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpXG4gICAgXG4jIE51bWJlciBvZiBvYmplY3RzIHRoYXQgaGF2ZSB0aGUgc2FtZSBjYXRlZ29yeVxudG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbClcbiAgICBcblxudG90YWxfcHJlY2VzaW9uIDwtIHRvdGFsX3ByZWNlc2lvbiArIGludGVyc2VjdGlvbiAvdG90YWxfc2FtZV9jbHVzdGVyXG50b3RhbF9yZWNhbGwgPC0gdG90YWxfcmVjYWxsICsgaW50ZXJzZWN0aW9uIC8gdG90YWxfc2FtZV9jYXRlZ29yeVxuICB9XG5cbiAgIyBjb21wdXRlIGF2ZyBwcmVjaXNpb24gYW5kIHJlY2FsbFxuICBwcmVjaXNpb24gPC0gdG90YWxfcHJlY2VzaW9uIC8gblxuICByZWNhbGwgPC0gdG90YWxfcmVjYWxsIC8gblxuXG4gIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSlcbn1cblxuIyBjb21wdXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbFxubWV0cmljcyA8LSBiY3ViZWQoZGF0YSlcblxuXG5wcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb25cbnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbFxuXG4jIFByaW50IHJlc3VsdHNcbmNhdChcXEJDdWJlZCBQcmVjaXNpb24gaXM6XFwsIHByZWNpc2lvbiwgXFxcXG5cXClcbmNhdChcXEJDdWJlZCBSZWNhbGwgaXM6XFwsIHJlY2FsbCwgXFxcXG5cXClcbmBgYFxuYGBgIn0= -->

```r
```r

#Use seed to guarantee replicability of random processes
set.seed(8953)

# run k-means clustering to find 3 clusters
kmeans.result <- kmeans(dataset3, 3)

# print the clusterng result
kmeans.result

# visualize clustering
fviz_cluster(kmeans.result, data = dataset3)

#average silhouette for each clusters
avg_sil <- silhouette(kmeans.result$cluster,dist(dataset3)) 
fviz_silhouette(avg_sil)

#Within-cluster sum of squares wss 
wss <- kmeans.result$tot.withinss
print(wss)

#BCubed 
kmeans_cluster <- c(kmeans.result$cluster)

ground_truth <- c(classLabel)

data <- data.frame(cluster = kmeans_cluster, label = ground_truth)

  bcubed <- function(data) {
  n <- nrow(data)
  total_precesion <- 0
  total_recall <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Number of objects in the same category and cluster
intersection <- sum(data$label[data$cluster == cluster] == label)
    
# Number of objects that are in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Number of objects that have the same category
total_same_category <- sum(data$label == label)
    

total_precesion <- total_precesion + intersection /total_same_cluster
total_recall <- total_recall + intersection / total_same_category
  }

  # compute avg precision and recall
  precision <- total_precesion / n
  recall <- total_recall / n

  return(list(precision = precision, recall = recall))
}

# compute BCubed precision and recall
metrics <- bcubed(data)


precision <- metrics$precision
recall <- metrics$recall

# Print results
cat(\BCubed Precision is:\, precision, \\n\)
cat(\BCubed Recall is:\, recall, \\n\)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


we can conclude from the graph and the results where k=3 is that the performance is good but worse than k=2, because there is overlapping between clusters. In addition, the recall is relatively low (0.45), However, the Precision is low (0.31) which could be duo to presence of outliers or sensitivity to Initial Centroid. We can also note that the WSS is 6451.51, indicating an intermidiate compactness of clusters, and that objects in a cluster are to some extent similar to one another. Lastly, the average silhouette width is 0.19 which reflects high inter-cluster similarity.

##### k=4


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuI1VzZSBzZWVkIHRvIGd1YXJhbnRlZSByZXBsaWNhYmlsaXR5IG9mIHJhbmRvbSBwcm9jZXNzZXNcbnNldC5zZWVkKDg5NTMpXG5cbiMgcnVuIGstbWVhbnMgY2x1c3RlcmluZyB0byBmaW5kIDQgY2x1c3RlcnNcbmttZWFucy5yZXN1bHQgPC0ga21lYW5zKGRhdGFzZXQzLCA0KVxuXG4jIHByaW50IHRoZSBjbHVzdGVybmcgcmVzdWx0XG5rbWVhbnMucmVzdWx0XG5cbiMgdmlzdWFsaXplIGNsdXN0ZXJpbmdcbmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gZGF0YXNldDMpXG5cbiNhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGVhY2ggY2x1c3RlcnNcbmF2Z19zaWwgPC0gc2lsaG91ZXR0ZShrbWVhbnMucmVzdWx0JGNsdXN0ZXIsZGlzdChkYXRhc2V0MykpIFxuZnZpel9zaWxob3VldHRlKGF2Z19zaWwpXG5cbiNXaXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcyB3c3MgXG53c3MgPC0ga21lYW5zLnJlc3VsdCR0b3Qud2l0aGluc3NcbnByaW50KHdzcylcblxuI0JDdWJlZFxua21lYW5zX2NsdXN0ZXIgPC0gYyhrbWVhbnMucmVzdWx0JGNsdXN0ZXIpXG5cbmdyb3VuZF90cnV0aCA8LSBjKGNsYXNzTGFiZWwpXG5cbmRhdGEgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0ga21lYW5zX2NsdXN0ZXIsIGxhYmVsID0gZ3JvdW5kX3RydXRoKVxuXG5cbiAgYmN1YmVkIDwtIGZ1bmN0aW9uKGRhdGEpIHtcbiAgbiA8LSBucm93KGRhdGEpXG4gIHRvdGFsX3ByZWNlc2lvbiA8LSAwXG4gIHRvdGFsX3JlY2FsbCA8LSAwXG5cbiAgZm9yIChpIGluIDE6bikge1xuICAgIGNsdXN0ZXIgPC0gZGF0YSRjbHVzdGVyW2ldXG4gICAgbGFiZWwgPC0gZGF0YSRsYWJlbFtpXVxuICAgIFxuIyBOdW1iZXIgb2Ygb2JqZWN0cyBpbiB0aGUgc2FtZSBjYXRlZ29yeSBhbmQgY2x1c3RlclxuaW50ZXJzZWN0aW9uIDwtIHN1bShkYXRhJGxhYmVsW2RhdGEkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbClcbiAgICBcbiMgTnVtYmVyIG9mIG9iamVjdHMgaW4gdGhlIHNhbWUgIGNsdXN0ZXJcbnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpXG4gICAgXG4jIE51bWJlciBvZiBvYmplY3RzIHRoYXQgaGF2ZSB0aGUgc2FtZSBjYXRlZ29yeVxudG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbClcbiAgICBcbiMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsIGZvciB0aGUgY3VycmVudCBpdGVtIGFuZCBhZGQgdGhlbSB0byB0aGUgc3Vtc1xudG90YWxfcHJlY2VzaW9uIDwtIHRvdGFsX3ByZWNlc2lvbiArIGludGVyc2VjdGlvbiAvdG90YWxfc2FtZV9jbHVzdGVyXG50b3RhbF9yZWNhbGwgPC0gdG90YWxfcmVjYWxsICsgaW50ZXJzZWN0aW9uIC8gdG90YWxfc2FtZV9jYXRlZ29yeVxuICB9XG5cbiAgIyBDb21wdXRlIGF2ZyBwcmVjaXNpb24gYW5kIHJlY2FsbFxuICBwcmVjaXNpb24gPC0gdG90YWxfcHJlY2VzaW9uIC8gblxuICByZWNhbGwgPC0gdG90YWxfcmVjYWxsIC8gblxuXG4gIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSlcbn1cblxuIyBjb21wdXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbFxubWV0cmljcyA8LSBiY3ViZWQoZGF0YSlcblxuXG5wcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb25cbnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbFxuXG4jIFByaW50IHJlc3VsdHNcbmNhdChcXEJDdWJlZCBQcmVjaXNpb24gaXM6XFwsIHByZWNpc2lvbiwgXFxcXG5cXClcbmNhdChcXEJDdWJlZCBSZWNhbGwgaXM6XFwsIHJlY2FsbCwgXFxcXG5cXClcbmBgYFxuYGBgIn0= -->

```r
```r
#Use seed to guarantee replicability of random processes
set.seed(8953)

# run k-means clustering to find 4 clusters
kmeans.result <- kmeans(dataset3, 4)

# print the clusterng result
kmeans.result

# visualize clustering
fviz_cluster(kmeans.result, data = dataset3)

#average silhouette for each clusters
avg_sil <- silhouette(kmeans.result$cluster,dist(dataset3)) 
fviz_silhouette(avg_sil)

#Within-cluster sum of squares wss 
wss <- kmeans.result$tot.withinss
print(wss)

#BCubed
kmeans_cluster <- c(kmeans.result$cluster)

ground_truth <- c(classLabel)

data <- data.frame(cluster = kmeans_cluster, label = ground_truth)


  bcubed <- function(data) {
  n <- nrow(data)
  total_precesion <- 0
  total_recall <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Number of objects in the same category and cluster
intersection <- sum(data$label[data$cluster == cluster] == label)
    
# Number of objects in the same  cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Number of objects that have the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
total_precesion <- total_precesion + intersection /total_same_cluster
total_recall <- total_recall + intersection / total_same_category
  }

  # Compute avg precision and recall
  precision <- total_precesion / n
  recall <- total_recall / n

  return(list(precision = precision, recall = recall))
}

# compute BCubed precision and recall
metrics <- bcubed(data)


precision <- metrics$precision
recall <- metrics$recall

# Print results
cat(\BCubed Precision is:\, precision, \\n\)
cat(\BCubed Recall is:\, recall, \\n\)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


we can conclude from the graph and the results where k=4 is that the performance is worse than k=2 and k=3, because there is a noticeable overlapping between clusters. Also, the clusers' space is pretty wide which results in a large distance between objects in the same cluster. In addition, the recall is relatively low (0.43) which might be a result of the overlapping and large distances between data objects. Furthermore, the Precision is low (0.29) which could be duo to presence of outliers or sensitivity to Initial Centroid. We can also note that the WSS is 5911.05 indicating a lower compactness of clusters. Lastly, the average silhouette width is 0.22 which is low reflecting high inter-cluster similarity.




## Clustering results

This table displays the results of clustering using various methods for each K.

+----------------------------------------+-------------------+----------------+-----------------------------+
|                                        | K=2               | K=3            | K=4                         |
+:======================================:+:=================:+:==============:+:===========================:+
| **Average Silhouette width**           | 0.34              | 0.31           | 0.2                         |
+----------------------------------------+-------------------+----------------+-----------------------------+
| **total within-cluster sum of square** | 7295.548          | 6778.046       | 5915.724                    |
+----------------------------------------+-------------------+----------------+-----------------------------+
| **BCubed precision**                   | 0.2812713         | 0.2807635      | 0.2895803                   |
+----------------------------------------+-------------------+----------------+-----------------------------+
| **BCubed recall**                      | 0.7064208         | 0.6786707      | 0.401187                    |
+----------------------------------------+-------------------+----------------+-----------------------------+
| **Visualization**                      | ![](images/k2-01) | ![](images/k3) | ![](images/k4){width="145"} |
+----------------------------------------+-------------------+----------------+-----------------------------+



## Phase 4




## Findings

While working on this project, we have prepared the data to actually implement data mining techniques on it, i.e: classification and clustering. As discussed in the previous section,


this table displays all results of the three classification algorithms (Gini index, information gain and gain ratio) with 3 different fold sizes (k=3,5,10)


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuXG4gXG5cbnByaW50KCBcbiAgcmJpbmQoXCIxMCBGb2xkc1wiID0gYyhcIiBcIiwgXCIgXCIsIFwiXCIsIFwiXCIpLCBcbiAgICAgICAgXCJnaW5pIGluZGV4XCIgPSBtYWNybyhnaW5pSW5kZXgxMGNtKSwgXG4gICAgICAgIFwiR2FpbiByYXRpb1wiID0gbWFjcm8oZ2FpblJhdGlvMTBjbSksIFxuICAgICAgICBcIkluZm9ybWF0aW9uIGdhaW5cIiA9IG1hY3JvKGluZm9HYWluMTBjbSksIFxuICAgICAgICBcIjUgRm9sZHNcIiA9IGMoXCIgXCIsIFwiIFwiLCBcIlwiLCBcIlwiKSwgXG4gICAgICAgIFwiZ2luaSBpbmRleFwiID0gbWFjcm8oZ2luaUluZGV4NWNtKSwgXG4gICAgICAgIFwiR2FpbiByYXRpb1wiID0gbWFjcm8oZ2FpblJhdGlvNWNtKSwgXG4gICAgICAgIFwiSW5mb3JtYXRpb24gZ2FpblwiID0gbWFjcm8oaW5mb0dhaW41Y20pLCBcbiAgICAgICAgXCIzIEZvbGRzXCIgPSBjKFwiIFwiLCBcIiBcIiwgXCJcIiwgXCJcIiksIFxuICAgICAgICBcImdpbmkgaW5kZXhcIiA9IG1hY3JvKGdpbmlJbmRleDNjbSksIFxuICAgICAgICBcIkdhaW4gcmF0aW9cIiA9IG1hY3JvKGdhaW5SYXRpbzNjbSksIFxuICAgICAgICBcIkluZm9ybWF0aW9uIGdhaW5cIiA9IG1hY3JvKGluZm9HYWluM2NtKSlcblxuKVxuYGBgIn0= -->

```r

 

print( 
  rbind("10 Folds" = c(" ", " ", "", ""), 
        "gini index" = macro(giniIndex10cm), 
        "Gain ratio" = macro(gainRatio10cm), 
        "Information gain" = macro(infoGain10cm), 
        "5 Folds" = c(" ", " ", "", ""), 
        "gini index" = macro(giniIndex5cm), 
        "Gain ratio" = macro(gainRatio5cm), 
        "Information gain" = macro(infoGain5cm), 
        "3 Folds" = c(" ", " ", "", ""), 
        "gini index" = macro(giniIndex3cm), 
        "Gain ratio" = macro(gainRatio3cm), 
        "Information gain" = macro(infoGain3cm))

)
NA
NA
NA
NA
NA

Based of this metrics we can further assess the performance of each method as follows: - Accuracy: the accuracy of gain ratio 10 folds is 63% which is higher than the others meaning that the model has successfully classified 63% of the instances. And comes after it the Gain ratio model with 5 folds with 62% accuracy. The worst was 3-fold gini index with only 56% accuracy

If we look at each fold separately, we notice that the gain ratio has the best performance according to all 4 metrics (accuracy, precision, sensitivity and specificity), so for the 10, 5, and 3 folds the gain ratio is the best. It can also be noticed that the 3-folds gini index was the worst in all aspects but the differences with the other models isn’t that high in most of the metrics.

So if we were to choose one among them it will be the 10-fold gain ratio. The gain ratio evaluated with 10-fold cross-validation appears to have the best performance among all the decision tree models. This might be because the gain ratio tends to favor unbalanced splits, where one partition is significantly smaller than the others. In our dataset, if an attribute has a rare value, the gain ratio may prioritize splitting on this attribute, despite the resulting imbalance.

Despite the superiority of the 10-fold cross-validation, the performance metrics of all three fold sizes (3-fold, 5-fold, and 10-fold) using the Gini index, gain ratio, and information gain are relatively similar. with the gain ratio giving best performance in each partition. This suggests that all three measures are robust within the context of this dataset. A likely contributing factor to this performance consistency is the balanced distribution of class labels in the dataset. When classes are balanced, each splitting criterion is more or less equally likely to encounter informative splits, which helps to decrease performance variability across different splitting methods.

It can also be noticed that all nine trees have selected the attribute “Experience Level” as the first splitting attribute, indicating that it is the strongest predictor in reducing uncertainty among all other predictors and the most informative in our case.

so, this is the model that we have chosen as our classification model (gain ratio with 10-folds)

plot(gainRatio10$finalModel)

As for clustering, using k-means method with k=2, the analysis highlighted that k=2 displayed superior performance, standing out for its distinct clusters without overlap. The data within each cluster exhibited significant similarity while being notably dissimilar from the other cluster, affirming k=2 as the optimal choice for this clustering scenario based on evaluation methods like BCubed Precision and Recall, Average Silhouette Width and based on graphs.

This table displays the results of clustering using various methods for each K.

K=2 K=3 K=4
Average Silhouette width 0.34 0.31 0.2
total within-cluster sum of square 7295.548 6778.046 5915.724
BCubed precision 0.2812713 0.2807635 0.2895803
BCubed recall 0.7064208 0.6786707 0.401187
Visualization

Based on these metrics, we can assess the performance of each K value:

Considering these metrics, we can conclude that K = 2 is the optimal k and performs comparatively better in terms of average silhouette width, BCubed precision, and BCubed recall. It indicates that the clustering with K = 2 leads to well-separated clusters without overlapping between clusters and with relatively good precision and recall. Which means the data in a cluster are close “similar” to each other and dissimilar to data in the other cluster “reflecting high intra-cluster similarity”. And the reason for the overlapping and the low average silhouette width , precision, and recall results in K=3 and K=4 is could be duo to presence of outliers or sensitivity to Initial Centroid and high inter-cluster similarity.

We can notice that these results that we have obtained from both calssification and clustering are interesting and strongly related to the core of the problem that we are working on, and will be directly reflecting on the solution.

Ultimately, both models prove valuable in predicting cybersecurity salaries and grouping the employees based on shared charactristics. The choice between classification and clustering hinges on specific objectives. Classification is advantageous for predicting salary ranges or categories based on known employee attributes, aiding individual compensation decisions. When it is essential to predict salary categories, employing the Gain ratio in classification proves beneficial. Clustering aids in identifying natural employee groupings, revealing trends and common characteristics, facilitating market segmentation, and informing broader strategic decisions. Pursuing the goal of uncovering natural groupings in salary data, K=2 clustering stands out for its distinct clusters.

In conclusion, both techniques are important and suitable for our dataset, duo to their ability to acheive our data mining tasks (predicting employees salaries and grouping them based on similarties). Thus both are critical to solve the problem introduced in this project, and both will help achieve our goals such as market segmentation, striking fairness among employees and increase their loyality.

In consequence, our solution is composed of two main parts that will help achieve our goals: 1- Use classification to predict employees’ salaries (using 10-fold gain ratio method) 2- Use clustering to group employees based on their similarties (using k-means with 2 clusters)

By addressing the issues, we can solve them such as unfairness, losing candidates to other companies that provide better privilages, and poor understanding of employees’ needs. Finally, by solving these problems, we can ensure the cybersecurity employees are satisfied and pleased with the salary they get, leading to a better performance at their jobs and better securing the organization’s data and valuable digital assets.

Refrences

[1] J. Han, M. Kamber, and J. Pei, “Data Mining: Concepts and Techniques,” 3rd ed., The Morgan Kaufmann Series in Data Management Systems.

[2] Y. Zhao, “R and Data Mining: Examples and Case Studies,” 1st ed. Academic Press, 2012. ISBN: 0123969638.

[3] Y. Zhao, “R and Data Mining,” RDataMining.com, Available: https://www.rdatamining.com/, Accessed on: November 23, 2023.

[4] M. Hahsler, “discretize {arules}R Documentation: Convert a Continuous Variable into a Categorical Variable,” R Project, Available: https://search.r-project.org/CRAN/refmans/arules/html/discretize.html, Accessed on: November 23, 2023.

LS0tDQp0aXRsZTogIkN5YmVyc2VjdXJpdHkgc2FsYXJpZXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UpDQoNCmBgYA0KDQojIyMgTmVlZGVkIGxpYnJhcmllcw0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGNvdW50cnljb2RlKQ0KbGlicmFyeShvdXRsaWVycykNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpsaWJyYXJ5KE5iQ2x1c3QpDQpsaWJyYXJ5KCJETXdSIikNCmxpYnJhcnkoIlJXZWthIikNCmxpYnJhcnkoIkM1MCIpDQpsaWJyYXJ5KCJycGFydCIpDQpsaWJyYXJ5KCJ0aGVtaXMiKQ0KbGlicmFyeShyYXR0bGUpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmBgYA0KDQojIHBoYXNlIDENCg0KIyMjIFByb2JsZW0gc3RhdGVtZW50DQoNClByZWRpY3Rpb24gb2YgY3liZXIgc2VjdXJpdHkgZW1wbG95ZWVzJyBzYWxhcmllcyBiYXNlZCBvbiAxMSBhdHRyaWJ1dGVzICYgZ3JvdXBpbmcgZW1wbG95ZWVzIGJhc2VkIG9uIHNoYXJlZCBjaGFyYWN0ZXJpc3RpY3MuDQoNCjEud29ya195ZWFyDQoNCjIuZXhwZXJpZW5jZV9sZXZlbA0KDQozLmVtcGxveW1lbnRfdHlwZQ0KDQo0LmpvYl90aXRsZQ0KDQo1LnNhbGFyeQ0KDQo2LnNhbGFyeV9jdXJyZW5jeQ0KDQo3LnNhbGFyeV9pbl91c2QNCg0KOC5lbXBsb3llZV9yZXNpZGVuY2UNCg0KOS5yZW1vdGVfcmF0aW8NCg0KMTAuY29tcGFueV9sb2NhdGlvbg0KDQoxMS5jb21wYW55X3NpemUNCg0KIyMjIFByb2JsZW0gZGVzY3JpcHRpb24NCg0KV2UgYXJlIGxpdmluZyBpbiB0aGUgImluZm9ybWF0aW9uIGFnZSIgb3IgcmF0aGVyIHRoZSAiZGF0YSBhZ2UiLCBtZWFuaW5nIHRoYXQgZXZlcnl0aGluZyBhcm91bmQgdXMgcmV2b2x2ZXMgYXJvdW5kIGRhdGEuIFRoZSBkYXRhIGhhcyBiZWNvbWUgb25lIG9mIHRoZSBtb3N0IHZhbHVhYmxlIGFzc2V0cyB0aGF0IGEgcGVyc29uIG9yIGFuIG9yZ2FuaXNhdGlvbiBjYW4gaGF2ZSwgc2luY2UgaXQgaGFzIGEgc2lnbmlmaWNhbnQgdmFsdWUsIGxvc2luZyBpdCB3aWxsIGxlYWQgdG8gc2lnbmlmaWNhbnQgZGFtYWdlcy4gVGh1cywgbW9zdCBvZiB0aGUgYXR0YWNrcyBub3dhZGF5cyBhcmUgZGlyZWN0ZWQgdG93YXJkIHRoZSBkYXRhLiBUbyBndWFyZCBhZ2FpbnN0IHN1Y2ggZGFtYWdlcywgb3JnYW5pc2F0aW9ucyBoYXZlIHJlYWxpc2VkIHRoZSBpbXBvcnRhbmNlIG9mIHByb3RlY3RpbmcgdGhlaXIgZGlnaXRhbCBhc3NldHMsIGxlYWRpbmcgdGhlbSB0byBoaXJlIGN5YmVyc2VjdXJpdHkgc3BlY2lhbGlzdHMuIFRoaXMgbWFkZSBjeWJlcnNlY3VyaXR5IGdhaW4gcG9wdWxhcml0eSBhbW9uZyBwZW9wbGUgc28gdGhlcmUncyBhIGdyb3dpbmcgdGVuZGVuY3kgdG8gc3R1ZHkgY3liZXJzZWN1cml0eS4gQ29uc2VxdWVudGx5IHRoaXMgcmVzdWx0ZWQgaW4gdGhlIGVtZXJnZW5jZSBvZiBwbGVudGlmdWwgcHJvZmVzc2lvbmFscyB3aXRoIHZhcmlvdXMgZXhwZXJpZW5jZSBsZXZlbHMgYW5kIHNraWxscyBpbiB0aGlzIGZpZWxkLiBBcyBhIHJlc3VsdCwgb3JnYW5pc2F0aW9ucyBtYXkgZmluZCBpdCBkaWZmaWN1bHQgdG8gZGVjaWRlIGEgc2FsYXJ5IGZvciBqb2IgY2FuZGlkYXRlcyBzb2xlbHkgYmFzZWQgb24gdGhlIENWLiBhbHNvLCBzaW5jZSB0aGUgYXR0YWNrcyBpbXByb3ZlIHJhcGlkbHksIG9yZ2FuaXNhdGlvbnMgbmVlZCB0byBoaXJlIG1vcmUgZW1wbG95ZWVzIGluIHRoZSBmYXIgZnV0dXJlIHRvIGRlZmVuZCBhZ2FpbnN0IHN1Y2ggYXR0YWNrcyBidXQgaXQncyBub3QgYW4gZWFzeSBtYXR0ZXIgdG8gcHJlZGljdCB0aGUgZnV0dXJlIHBheXJvbGwgd2hpY2ggbWF5IGhpbmRlcnMgc29tZSBvZiB0aGUgb3JnYW5pc2F0aW9uJ3MgcGxhbnMuIEFub3RoZXIgaXNzdWUgYXJpc2VzIHdoZW4gdGhlIGRlY2lzaW9uIG1ha2VycyBpbiB0aGUgb3JnYW5pc2F0aW9uIGFyZW4ndCBmdWxseSBhd2FyZSBvZiB0aGUgZGlmZmVyZW50IGdyb3VwcyBvZiBlbXBsb3llZXMgYW5kIHRoZWlyIGRpZmZlcmludCBuZWVkcy4gVGhlaXIgbGFjayBvZiBhd2FyZW5lc3MgZ2l2ZXMgYSBjaGFuY2UgZm9yIHRoZSBjb21wZXRpdG9yIG9yZ2FuaXNhdGlvbnMgdG8gYXR0cmFjdCB0aGVpciBlbXBsb3llZXMgdG8gdGhlbSBieSBvZmZlcmluZyBhIGJldHRlciBzYWxhcnkgYW5kIHByaXZpbGFnZXMgdGhhdCBtYXRjaCB0aGVpciBuZWVkcy4NCg0KIyMjIERhdGEgbWluaW5nIHRhc2sNCg0KUHJlZGljdGlvbiBvZiB0aGUgY3liZXIgc2VjdXJpdHkgZW1wbG95ZWVzJyBzYWxhcnkgY2F0ZWdvcmllcyAoVmVyeSBMb3csIExvdywgLCBIaWdoLCBWZXJ5IEhpZ2gpIHVzaW5nIGNsYXNzaWZpY2F0aW9uLCBhbmQgZGVzY3JpcHRpb24gb2YgZGF0YSBjaGFyYWN0ZXJpc3RpY3MgYW5kIGJlaGF2aW9yIGFuZCBncm91cGluZyBkYXRhIHVzaW5nIGNsdXN0ZXJpbmcgbWV0aG9kcy4NCg0KIyMjIEdvYWwNCg0KR2l2ZW4gdGhlIHByb2JsZW1zIHdlIGRpc2N1c3NlZCBhbmQgSW4gb3JkZXIgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhpcyBmaWVsZCwgd2UgZGVjaWRlZCB0byBhbmFseXNlIGEgZGF0YXNldCBvZiAxMjQ3IGN5YmVyc2VjdXJpdHkgZW1wbG95ZWVzLCBjb250YWluaW5nIGluZm9ybWF0aW9uIHN1Y2ggYXMgc2FsYXJ5LCBqb2IgdGl0bGUsIGFuZCBleHBlcmllbmNlIGxldmVsLiBBbmFseXNpbmcgdGhpcyBkYXRhc2V0IGNhbiBwcm92aWRlIGluc2lnaHRmdWwgcHJlZGljdGlvbnMgcmVnYXJkaW5nIHRoZSBzYWxhcnkgcmFuZ2Ugb2YgYSBjeWJlcnNlY3VyaXR5IGVtcGxveWVlIGFuZCBkZXNjcmlwdGlvbiBvZiB0aGUgY3liZXJzZWN1cml0eSBtYXJrZXQgYmVoYXZpb3IgYnkgZ3JvdXBpbmcgdGhlIGRhdGEsIHdoaWNoIGNhbiBoZWxwIGluOg0KDQotICAgTWFya2V0IHNlZ21lbnRhdGlvbg0KLSAgIElkZW50aWZ5IHRyZW5kcw0KLSAgIFNwZWNpZnlpbmcgY29tbW9uIGNoYXJhY3RyZXN0aWNzIGFtb25nIGN5YmVyc2VjdXJpdHkgZW1wbG95ZWVzDQotICAgSWRlbnRpZnkgdGhlIG1haW4gY3liZXJzZWN1cml0eSBlbXBsb3llZSBncm91cHMgZm9yIGJldHRlciB1bmRlcnN0YW5kaW5nIHRoZWlyIG5lZWRzDQotICAgTWFraW5nIGJldHRlciBkZWNpc2lvbnMNCi0gICBNYWtpbmcgcmVjcnVpdG1lbnQgYW5kIGhpcmluZyBwcm9jZXNzIGVhc2llciBhbmQgbW9yZSBlZmZpY2llbnQNCi0gICBQcmVkaWN0aW5nIHRoZSBmdXR1cmUgcGF5cm9sbA0KLSAgIEluY3JlYXNpbmcgbG95YWx0eQ0KLSAgIEluY3JlYXNpbmcgdGhlIHNhdGlzZmFjdGlvbiByYXRlDQotICAgQWNoaWV2aW5nIGZhaXJuZXNzDQojIyBEYXRhDQoNCiMjIFNvdXJjZSBvZiBkYXRhOg0KDQo8aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9kZWVwY29udHJhY3Rvci9jeWJlci1zZWN1cml0eS1zYWxhcmllcz4NCg0KIyMjIFJlYWRpbmcgYW5kIHZpZXdpbmcgZGF0YXNldA0KDQpgYGB7cn0NCmRhdGFzZXQ9IHJlYWQuY3N2KHVybCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1NhcmFoQWxoaW5kaS9ETV9wcm9qZWN0L21haW4vRGF0YSUyMFNldC9zYWxhcmllc19jeWJlci5jc3YiKSwgaGVhZGVyPVRSVUUpDQpWaWV3KGRhdGFzZXQpDQoNCmBgYA0KDQojIyMgT3JpZ2luYWwgZGF0YXNldA0KDQp3ZSB3aWxsIGtlZXAgYSBjb3B5IG9mIHRoZSBvcmlnaW5hbCBkYXRhc2V0IGJlZm9yZSBkYXRhIHByZXByb2Nlc3NpbmcgdG8gdXNlIGlmIG5lZWRlZCBhdCBhbnkgdGltZQ0KDQpgYGB7cn0NCm9yaWdpbmFsRGF0YXNldD0gZGF0YXNldA0KYGBgDQoNCiMjIEdlbmVyYWwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRhdGFzZXQ6DQoNCk5vLiBvZiBhdHRyaWJ1dGVzOiAxMVwNClR5cGUgb2YgYXR0cmlidXRlczogT3JkaW5hbCAsIE5vbWluYWwsIGFuZCBOdW1lcmljXA0KTm8uIG9mIG9iamVjdHM6IDEyNDdcDQpDbGFzcyBsYWJlbDogc2FsYXJ5X2luX3VzZA0KDQpgYGB7cn0NCm5jb2woZGF0YXNldCkNCm5yb3coZGF0YXNldCkNCm5hbWVzKGRhdGFzZXQpDQpzdHIoZGF0YXNldCkNCmBgYA0KDQojIyMgQXR0cmlidXRlcycgZGVzY3JpcHRpb24gdGFibGUNCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKkF0dHJpYnV0ZSBOYW1lKiogfCAqKkRlc2NyaXB0aW9uKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICoqRGF0YSBUeXBlKiogfCAqKlBvc3NpYmxlIHZhbHVlcyoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKz09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCB3b3JrX3llYXIgICAgICAgICAgfCBUaGUgeWVhciBpbiB3aGljaCBzYWxhcnkgd2FzIHJlY29yZGVkICAgICAgICAgICAgICAgICAgICAgICB8IE51bWVyaWNhbCAgICAgfCAyMDIwIHRvIDIwMjIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBleHBlcmllbmNlX2xldmVsICAgfCBFeHBlcnRpc2UgbGV2ZWwgb2YgdGhlIGVtcGxveWVlICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE9yZGluYWwgICAgICAgfCBFbiAiRW50cnkgbGV2ZWwiXCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBNSSAiTWlkIGxldmVsIlwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBTRSAiU2VuaW9yIGxldmVsIlwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBFWCAiRXhlY3V0aXZlIGxldmVsIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBlbXBsb3ltZW50X3R5cGUgICAgfCBUaGUgbmF0dXJlIG9yIGNhdGVnb3J5IG9mIGVtcGxveWVlJ3MgZW5nYWdlbWVudCBpbiB0aGUgam9iICB8IE5vbWluYWwgICAgICAgfCBQVCAiUGFydCB0aW1lIlwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBGVCAiRnVsbCB0aW1lIlwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBDVCAiQ29udHJhY3RcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBGTCJGcmVlbGFuY2VyIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBqb2JfdGl0bGUgICAgICAgICAgfCBUaGUgcm9sZSB3b3JrZWQgaW4gZHVyaW5nIHRoZSB5ZWFyICAgICAgICAgICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICAgICAgfCBEaWZmZXJlbnQgdGl0bGVzLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBsaWtlIFNlY3VyaXR5IEFuYWx5c3QsIHNlY3VyaXR5IHJlc2VhcmNoZXIgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBzYWxhcnkgICAgICAgICAgICAgfCBUaGUgdG90YWwgZ3Jvc3Mgc2FsYXJ5IGFtb3VudCBwYWlkICAgICAgICAgICAgICAgICAgICAgICAgICB8IE51bWVyaWNhbCAgICAgfCAxNzQwLTUwMDAxNTY2ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBzYWxhcnlfY3VycmVuY3kgICAgfCBUaGUgY3VycmVuY3kgb2YgdGhlIHNhbGFyeSBwYWlkIHRvIHRoZSBlbXBsb3llZSAgICAgICAgICAgICB8IE5vbWluYWwgICAgICAgfCBEaWZmZXJlbnQgY3VycmVuY2llcyBhY2NvcmRpbmcgdG8gSVNPIDQyMTcgY3VycmVuY3kgY29kZS4gfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBsaWtlIERFLENBICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBzYWxhcnlfaW5fdXNkICAgICAgfCBUaGUgc2FsYXJ5IHBhaWQgaW4gVW5pdGVkIHN0YXRlcyBkb2xsYXIgICAgICAgICAgICAgICAgICAgICB8IE51bWVyaWNhbCAgICAgfCAyMDAwIHRvIDM2NTU5Ni40MCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBlbXBsb3llZV9yZXNpZGVuY2UgfCBFbXBsb3llZSdzIHByaW1hcnkgY291bnRyeSBvZiByZXNpZGVuY2UgICAgICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICAgICAgfCBEaWZmZXJlbnQgY291bnRyaWVzLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBsaWtlIFVTLEFFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCByZW1vdGVfcmF0aW8gICAgICAgfCBQZXJjZW50YWdlIG9mIG9ubGluZSB3b3JrIGJ5IGVtcGxveWVlIGluIHRoZSBzcGVjaWZpZWQgeWVhciB8IE51bWVyaWNhbCAgICAgfCAwICJObyByZW1vdGUgd29yayJcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCA1MCAiUGFydGlhbGx5IHJlbW90ZSJcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCAxMDAgIkZ1bGx5IHJlbW90ZSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBjb21wYW55X2xvY2F0aW9uICAgfCBUaGUgY291bnRyeSBvZiB0aGUgZW1wbG95ZXIncyBtYWluIG9mZmljZSAgICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICAgICAgfCBEaWZmZXJlbnQgY291bnRyaWVzLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBsaWtlIEJSLEJXICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBjb21wYW55X3NpemUgICAgICAgfCBIb3cgYmlnL3NtYWxsIGlzIHRoZSBjb21wYW55ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE9yZGluYWwgICAgICAgfCBTICwgTSBvciBMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQojIHBoYXNlIDINCg0KIyMjIHNhbXBsZSBvZiAyMCBlbXBsb3llZXMgZnJvbSB0aGUgZGF0YXNldDoNCg0KdXNpbmcgc2FtcGxlX24odGFibGUsc2l6ZSkgZnVuY3Rpb24gYW5kIHVzaW5nIChzZXRfc2VlZCgpKQ0KDQpgYGB7cn0NCnNldC5zZWVkKDMwKQ0Kc2FtcGxlPXNhbXBsZV9uKGRhdGFzZXQsMjApDQpwcmludChzYW1wbGUpDQpgYGANCg0KIyMjIFNob3cgdGhlIG1pc3NpbmcgdmFsdWU6DQoNCmlmIGl0IGlzIEZBTFNFIGl0IG1lYW5zIG5vIG51bGwgdmFsdWUsaWYgaXQgaXMgVFJVRSB0aGVyZSBpcyBudWxsIHZhbHVlLiBJbiBvdXIgZGF0YXNldCB0aGVyZSBpcyBubyBudWxsIHZhbHVlcy4NCg0KYGBge3J9DQppcy5uYShkYXRhc2V0KQ0Kc3VtKGlzLm5hKGRhdGFzZXQpKQ0KYGBgDQoNCiMjIyBTaG93IHRoZSBNaW4uLDFzdCBRdS4sTWVkaWFuLE1lYW4gLDNyZCBRdS4sTWF4LiBmb3IgZWFjaCBudW1lcmljIGNvbHVtbg0KDQpUaGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgZGF0YXNldCB2YXJpYWJsZXMgcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHRoZSBkaXN0cmlidXRpb24gb2YgZmVhdHVyZXMuIHdlIGNhbiBjb25jbHVkZSB0aGUgZm9sbG93aW5nOg0KDQpJbiB3b3JrX3llYXI6IFRoZSBkYXRhIHNwYW5zIGZyb20gdGhlIHllYXIgMjAyMCB0byAyMDIyIHdpdGggTW9zdCBkYXRhIGZhbGxpbmcgd2l0aGluIHRoZSB5ZWFycyAyMDIxIGFuZCAyMDIyLCBhcyBpbmRpY2F0ZWQgYnkgYm90aCB0aGUgbWVkaWFuIGFuZCBtZWFuIGJlaW5nIGNlbnRlcmVkIGFyb3VuZCAyMDIxLg0KDQpJbiBzYWxhcnk6IFNhbGFyaWVzIHZhcnkgd2lkZWx5IHdpdGggYSBtaW5pbXVtIG9mIFwkMSw3NDAgYW5kIGEgbWF4aW11bSBvZiBcJDUwMCBtaWxsaW9uLiBUaGUgbWVkaWFuIGlzIFwkMTIwLDAwMCB3aGljaCBpcyBhIG1pZCB2YWx1ZSwgYnV0IHRoZSBtZWFuIGlzIG5vdGFibHkgaGlnaGVyIGF0IFwkNTYwLDg1MiB3aGljaCBtaWdodCBiZSBkdW8gdG8gZXh0cmVtZSB2YWx1ZXMgb3Igbm90YWJsZSBza2V3bmVzcy4NCg0KSW4gc2FsYXJ5X2luX3VzZDogVGhlIGRhdGEgaGFzIGEgbWVkaWFuIG9mIFwkMTEwLDAwMCwgYW5kIGEgbWVhbiBvZiBcJDEyMCwyNzgsIGFuZCB0aGUgc3ByZWFkIG9mIHNhbGFyaWVzIGlzIG9ic2VydmFibGUgaW4gdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVkaWFuIGFuZCBtZWFuLg0KDQpJbiByZW1vdGVfcmF0aW86IEluZGljYXRlcyB0aGUgcGVyY2VudGFnZSBvZiByZW1vdGUgd29yayByYW5naW5nIGZyb20gMCUgdG8gMTAwJSwgd2l0aCBhIG1lZGlhbiBhbmQgM3JkIHF1YXJ0aWxlIGF0IDEwMCUsIGFuZCBhIG1lYW4gb2YgNzEuNDklLCBpbmRpY2F0aW5nIGEgbm90YWJsZSBwcmVzZW5jZSBvZiByZW1vdGUgd29yayBpbiB0aGUgZGF0YXNldCwgc3VnZ2VzdGluZyBzb21lIHZhcmlhYmlsaXR5Lg0KDQpgYGB7cn0NCnN1bW1hcnkoZGF0YXNldCR3b3JrX3llYXIpDQpzdW1tYXJ5KGRhdGFzZXQkc2FsYXJ5KQ0Kc3VtbWFyeShkYXRhc2V0JHNhbGFyeV9pbl91c2QpDQpzdW1tYXJ5KGRhdGFzZXQkcmVtb3RlX3JhdGlvKQ0KYGBgDQoNCiMjIyBTaG93IHRoZSB2YXJpYW5lIG9mIGVhY2ggbnVtZXJpYyBjb2x1bW4NCg0KdmFyaWFuY2UgaXMgdG8gdW5kZXJzdGFuZCB0aGUgc3ByZWFkIG9yIGRpc3BlcnNpb24gb2YgdGhlIHZhbHVlcyBpbiBlYWNoIGNvbHVtbi4gQSBoaWdoZXIgdmFyaWFuY2UgaW5kaWNhdGVzIHRoYXQgdGhlIHZhbHVlcyBhcmUgbW9yZSBzcHJlYWQgb3V0IGZyb20gdGhlIG1lYW4gYW5kIGluIG91ciBkYXRhc2V0IHRoZSBoaWdoZXN0IHZhcmllZCBhdHRyaWJ1dGUgaXMgc2FsYXJ5LCB3aGlsZSBhIGxvd2VyIHZhcmlhbmNlIGluZGljYXRlcyB0aGF0IHRoZSB2YWx1ZXMgYXJlIGNsb3NlciB0byB0aGUgbWVhbiB3aGljaCBpbiBvdXIgZGF0YXMgaXQgaXMgd29yayB5ZWFyIGF0dHJpYnV0ZS4NCg0KVmFyaWFuY2UgcmVzdWx0cyByZXZlYWwgdGhhdDogLXdvcmsgeWVhcnMgYXJlIHRvIHNvbWUgZXh0ZW50IGNvbnNpc3RlbnQgLXNhbGFyaWVzIHNob3cgbm90YWJsZSB2YXJpYWJpbGl0eSBhbmQgcG9zc2libGUgb3V0bGllcnMgLXNhbGFyaWVzIGluIFVTRCBoYXZlIGEgc3RhYmxlIGRpc3RyaWJ1dGlvbiAtcmVtb3RlIHdvcmsgcmF0aW8gaGF2ZSBtb2RlcmF0ZSB2YXJpYWJpbGl0eQ0KDQpgYGB7cn0NCnZhcihkYXRhc2V0JHdvcmtfeWVhcikNCnZhcihkYXRhc2V0JHNhbGFyeSkNCnZhcihkYXRhc2V0JHNhbGFyeV9pbl91c2QpDQp2YXIoZGF0YXNldCRyZW1vdGVfcmF0aW8pDQpgYGANCg0KIyMjIFZpc3VhbGl6YXRpb24gb2YgcmVsYXRpb25zaGlwIGJldHdlZW4gc29tZSBwYWlycyBvZiBhdHRyaWJ1dGVzOg0KDQpIZXJlIHdlIHVzZWQgYm94cGxvdCB0byBzZWUgdGhlIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIHNhbGFyeV9pbl91c2QgYW5kIGV4cGVyaWVuY2VfbGV2ZWwgV2Ugb2JzZXJ2ZWQgdGhhdCBzYWxhcmllcyB2YXJ5IGRlcGVuZGluZyBvbiB0aGUgbGV2ZWwgb2YgZXhwZXJpZW5jZSx0aGV5IGFyZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQuDQoNCmBgYHtyfQ0KYm94cGxvdChzYWxhcnlfaW5fdXNkIH4gZXhwZXJpZW5jZV9sZXZlbCwgZGF0YSA9IGRhdGFzZXQgLCB5YXh0PSJuIikNCmxhYmVsczwtIHByZXR0eShkYXRhc2V0JHNhbGFyeV9pbl91c2QpDQpsYWJlbHM8LSBzYXBwbHkobGFiZWxzLCBmdW5jdGlvbih4KSBmb3JtYXQoeCwgc2NpZW50aWZpYyA9IEZBTFNFKSkNCmF4aXMoc2lkZSA9IDIsIGF0PXByZXR0eShkYXRhc2V0JHNhbGFyeV9pbl91c2QpLCBsYWJlbHMgPSBsYWJlbHMgKQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpDQpgYGANCg0KSGVyZSB3ZSB1c2VkIGJveHBsb3QgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gYmV0d2VlbiBzYWxhcnlfaW5fdXNkIGFuZCB3b3JrX3llYXIgV2Ugb2JzZXJ2ZWQgdGhhdCAyMDIxIHNhbGFyaWVzIHdlcmUgY2xvc2UgdG8gZWFjaCBvdGhlciBidXQgaW4gMjAyMiB0aGUgZ2FwIGJldHdlZW4gdGhlbSBnZXR0aW5nIGJpZ2dlci4NCg0KYGBge3J9DQpib3hwbG90KHNhbGFyeV9pbl91c2QgfiB3b3JrX3llYXIsIGRhdGEgPSBkYXRhc2V0ICwgeWF4dD0ibiIpDQpsYWJlbHM8LSBwcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKQ0KbGFiZWxzPC0gc2FwcGx5KGxhYmVscywgZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBGQUxTRSkpDQpheGlzKHNpZGUgPSAyLCBhdD1wcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKSwgbGFiZWxzID0gbGFiZWxzICkNCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KYGBgDQoNCkhlcmUgd2UgdXNlZCBib3hwbG90IHRvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIGJldHdlZW4gc2FsYXJ5X2luX3VzZCBhbmQgZW1wbG95bWVudF90eXBlIFdlIG9ic2VydmVkIHRoYXQgRnVsbCBUaW1lIChGVCkgb2ZmZXJzIG1vcmUgc2FsYXJ5IHRoYW4gdGhlIG90aGVyIGNhdGVnb3JpZXMuDQoNCmBgYHtyfQ0KYm94cGxvdChzYWxhcnlfaW5fdXNkIH4gZW1wbG95bWVudF90eXBlLCBkYXRhID0gZGF0YXNldCAsIHlheHQ9Im4iKQ0KbGFiZWxzPC0gcHJldHR5KGRhdGFzZXQkc2FsYXJ5X2luX3VzZCkNCmxhYmVsczwtIHNhcHBseShsYWJlbHMsIGZ1bmN0aW9uKHgpIGZvcm1hdCh4LCBzY2llbnRpZmljID0gRkFMU0UpKQ0KYXhpcyhzaWRlID0gMiwgYXQ9cHJldHR5KGRhdGFzZXQkc2FsYXJ5X2luX3VzZCksIGxhYmVscyA9IGxhYmVscyApDQpvcHRpb25zKHNjaXBlbiA9IDk5OSkNCmBgYA0KDQpIZXJlIHdlIHVzZWQgYm94cGxvdCB0byBzZWUgdGhlIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIHNhbGFyeV9pbl91c2QgYW5kIGNvbXBhbnlfc2l6ZSBXZSBvYnNlcnZlZCB0aGF0IHRoZSBsYXJnZXIgdGhlIGNvbXBhbnkgaXMgdGhlIGhpZ2hlciB0aGUgc2FsYXJ5IHdhcy4NCg0KYGBge3J9DQpib3hwbG90KHNhbGFyeV9pbl91c2QgfiBjb21wYW55X3NpemUsIGRhdGEgPSBkYXRhc2V0ICwgeWF4dD0ibiIpDQpsYWJlbHM8LSBwcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKQ0KbGFiZWxzPC0gc2FwcGx5KGxhYmVscywgZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBGQUxTRSkpDQpheGlzKHNpZGUgPSAyLCBhdD1wcmV0dHkoZGF0YXNldCRzYWxhcnlfaW5fdXNkKSwgbGFiZWxzID0gbGFiZWxzICkNCm9wdGlvbnMoc2NpcGVuID0gOTk5KSANCmBgYA0KDQojIyBEYXRhIHByZXByb2NjZXNzaW5nDQoNCiMjIERhdGEgUmVkdWN0aW9uDQoNCiMjIyBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24NCg0KVGhlICJzYWxhcnkiIGNvbHVtbiBnaXZlcyB0aGUgc2FtZSBpbmZvcm1hdGlvbiBhcyAic2FsYXJ5X2luX3VzZCIgaXQncyBqdXN0IGEgbWF0dGVyIG9mIGN1cnJlbmN5IGV4Y2hhbmdlLCBhbmQgd2Ugd2lsbCBldmVudHVhbGx5IHRyYW5zZm9ybSBhbGwgdGhlIHZhbHVlcyBpbiAic2FsYXJ5IiBjb2x1bW4gdG8gb25lIGNvbW1vbiBjdXJyZW5jeSBzbyB3ZSBjYW4gcHJvcGVybHkgZGVhbCB3aXRoIHRoZW0uIFRvIGZ1cnRoZXIgY29uZmlybSB0aGF0IHRoZSB0d28gY29sdW1uIGFyZSByZWR1bmRhbnQsIHdlIHdpbGwgdXNlIHRoZSBsYXRlc3QgZXhjaGFuZ2UgcmF0ZSBmb3IgVVNEIHRvIHRoZSBkZXNpcmVkIGN1cnJlbmN5Lg0KDQp3ZSB3aWxsIHN0YXJ0IGJ5IGNyZWF0aW5nIGEgdGVtcG9yYXJ5IGNvbHVtbiBuYW1lZCAiY29udmVydGVkX3NhbGFyeSIgdG8gc2F2ZSB0aGUgc2FsYXJ5IHRoYXQgd2Ugd2lsbCBnZXQgYnkgdXNpbmcgdGhlIGV4Y2hhbmdlIHJhdGUgdG8gY29udmVydCB0aGUgc2FsYXJ5X2luX3VzZCB0byB0aGUgc2FsYXJ5IHdpdGggZGlmZmVyZW50IGN1cnJlbmNpZXMgdG8gY29tcGFyZSB3aXRoICJzYWxhcnkiIGNvbHVtbg0KDQpgYGB7cn0NCmNvbnZlcnRlZERhdGFzZXQ9ZGF0YXNldA0KDQoNCmNvbnZlcnRlZERhdGFzZXQkZXhjaGFuZ2VfcmF0ZSA9IGZhY3Rvcihjb252ZXJ0ZWREYXRhc2V0JHNhbGFyeV9jdXJyZW5jeSwgbGV2ZWxzPWMoIlVTRCIsIkJSTCIsIkdCUCIsIkVVUiIsIklOUiIsIkNBRCIsIkNIRiIsIkRLSyIsIlNHRCIsIkFVRCIsIlNFSyIsIk1YTiIsIklMUyIsIlBMTiIsIk5PSyIsIklEUiIsIk5aRCIsIkhVRiIsIlpBUiIsIlRXRCIsIlJVQiIpLCBsYWJlbHM9YygxLzEsMS8wLjIwLDEvMS4yMiwxLzEuMDYsMS8wLjAxMiwxLzAuNzQsMS8xLjEwLDEvMC4xNCwxLzAuNzMsMS8wLjY0LDEvMC4wOTAsMS8wLjA1NywxLzAuMjYsMS8wLjIzLDEvMC4wOTMsMS8wLjAwMDA2NSwxLzAuNjAsMS8wLjAwMjcsMS8wLjA1MywxLzAuMDMxLDEvMC4wMTApKQ0KY29udmVydGVkRGF0YXNldCRleGNoYW5nZV9yYXRlID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoY29udmVydGVkRGF0YXNldCRleGNoYW5nZV9yYXRlKSkNCmNvbnZlcnRlZERhdGFzZXQkY29udmVydGVkX3NhbGFyeSA9IGNvbnZlcnRlZERhdGFzZXQkc2FsYXJ5X2luX3VzZCpjb252ZXJ0ZWREYXRhc2V0JGV4Y2hhbmdlX3JhdGUNCg0KDQoNCnNldC5zZWVkKDEpDQpzYWxhcnlfc2FtcGxlIDwtIHNhbXBsZV9uKGNvbnZlcnRlZERhdGFzZXRbLGMoInNhbGFyeSIsImNvbnZlcnRlZF9zYWxhcnkiKV0sMTApDQoNCnByaW50KHNhbGFyeV9zYW1wbGUpDQpgYGANCg0KYXMgc2hvd24gaW4gdGhlIHNhbXBsZSwgdGhlIHR3byBjb2x1bW5zIGFyZSBhbG1vc3QgaWRlbnRpY2FsLiBUaGlzIGNhbiBiZSBwcm92ZWQgYnkgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYXMgd2VsbC4NCg0KYGBge3J9DQpjb3JyZWxhdGlvbiA8LSBjb3IoY29udmVydGVkRGF0YXNldCRzYWxhcnkgLCBjb252ZXJ0ZWREYXRhc2V0JGNvbnZlcnRlZF9zYWxhcnkpDQpwcmludChjb3JyZWxhdGlvbikNCmBgYA0KDQpUaGUgY29ycmVsYXRpb24gaXMgc28gaGlnaCBidXQgaXQgaGFzbid0IHJlYWNoZWQgMTAwJSBwb3NzaWJseSBkdWUgdG8gcm91bmRpbmcgaW4gdGhlIGNhbGN1bGF0aW9ucyBhbmQgc2xpZ2h0IGRpZmZlcmVuY2VzIGluIHRoZSBleGNoYW5nZSByYXRlIG92ZXIgdGltZS4NCg0KVG8gbWFrZSB0aGUgbWluaW5nIHByb2Nlc3MgbW9yZSBlZmZpZWNlbnQgYW5kIGhhcyBhbiBpbXByb3ZlZCBxdWFsaXR5LCB3ZSBkZWNpZGVkIHRvIHJlbW92ZSB0aGUgInNhbGFyeSIgY29sdW1uLg0KDQpgYGB7cn0NCmRhdGFzZXQ8LWRhdGFzZXRbLC1jKDUpXQ0KYGBgDQoNCiMjIyBGaW5kIHRoZSBvdXRsaWVycyBhbmQgcmVtb3ZlIHRoZW06DQoNCldlIHdpbGwgc2hvdyBvdXRsaWVycyB3aXRoIGJveFBsb3RzIGFuZCB0aGVuIHJlbW92ZSB0aGVtLCB0byBtaW5pbWl6ZSBub2lzZSBhbmQgdG8gZ2V0IGJldHRlciBhbmFseXRpY2FsIHJlc3VsdHMgd2hlbiBhcHBseWluZyBkYXRhIG1pbmluZyB0ZWNobmlxdWVzLg0KDQpub3cgd2Ugc2hvdyAoc2FsYXJ5X2luX3VzZCkgYXR0cmlidXRlcycgb3V0bGllcnMuIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgbWFueSBvdXRsaWVycyB3aXRoIGV4Y2VwdGlvbmFsbHkgaGlnaCB2YWx1ZXMsIHRodXMgd2Ugd2lsbCByZW1vdmUgdGhlbS4NCg0KYGBge3J9DQpib3hwbG90KGRhdGFzZXQkc2FsYXJ5X2luX3VzZCkNCg0KDQoNCk91dFNhbGFyeSA9IG91dGxpZXIoZGF0YXNldCRzYWxhcnlfaW5fdXNkLCBsb2dpY2FsID1UUlVFKQ0KRmluZF9vdXRsaWVyID0gd2hpY2goT3V0U2FsYXJ5ID09VFJVRSwgYXJyLmluZCA9IFRSVUUpDQpkYXRhc2V0PSBkYXRhc2V0Wy1GaW5kX291dGxpZXIsXQ0KDQpgYGANCg0Kbm93IHdlIHNob3cgKHJlbW90ZV9yYXRpbykgYXR0cmlidXRlcycgb3V0bGllcnMuIHdlIGNhbiBzZWUgdGhlcmUgYXJlbid0IG91dGxpZXJzIGluIHJlbW90ZV9yYXRpbywgdGh1cyB3ZSBkb24ndCBuZWVkIHRoZSBsYXN0IHN0ZXAgaS5lOiByZW1vdmluZyBvdXRsaWVycycgcm93cy4NCg0KYGBge3J9DQpib3hwbG90KGRhdGFzZXQkcmVtb3RlX3JhdGlvKQ0KDQpgYGANCg0Kbm93IHdlIHNob3cgKHdvcmtfeWVhcikgYXR0cmlidXRlcycgb3V0bGllcnMuIHdlIGNhbiBzZWUgdGhlcmUgYXJlbid0IG91dGxpZXJzIGluIHdvcmtfeWVhciwgdGh1cyB3ZSBkb24ndCBuZWVkIHRoZSBsYXN0IHN0ZXAgaS5lOiByZW1vdmluZyBvdXRsaWVycycgcm93cy4NCg0KYGBge3J9DQpib3hwbG90KGRhdGFzZXQkd29ya195ZWFyKQ0KDQpgYGANCg0KIyMjIENvbmNlcHQgaGllcmFyY2h5IGdlbmVyYXRpb24gZm9yIG5vbWluYWwgZGF0YQ0KDQp0aGUgY29sdW1ucyAiY29tcGFueV9sb2NhdGlvbiIgYW5kICJlbXBsb3llZV9yZXNpZGVuY2UiIGhhdmUgdGhlIG5hbWUgb2YgY291bnRyaWVzIGZvciB0aGUgY29tcGFueSBhbmQgZW1wbG95ZWUgcmVzcGVjdGl2ZWx5LiBBbmQgdGhlc2UgYXR0cmlidXRlcyBjYW4gYmUgZ2VuZXJhbGl6ZWQgdG8gaGlnaGVyLWxldmVsIGNvbmNlcHQgdGhhdCBpcyByZWdpb24gdG8gaGVscCB1bmRlcnN0YW5kIGFuZCBhbmFseXplIHRoZSBkYXRhc2V0IGJldHRlciBhbmQgaW1wcm92ZSBhbGdvcml0aG0gcGVyZm9ybWFuY2UuDQoNCldlIHdpbGwgdXNlIHRoZSA3IHJlZ2lvbnMgYXMgZGVmaW5lZCBpbiB0aGUgV29ybGQgQmFuayBEZXZlbG9wbWVudCBJbmRpY2F0b3JzLiBUaGVzZSByZWdpb25zIGFyZToNCg0KMS4gIEVhc3QgQXNpYSBhbmQgUGFjaWZpYzogVGhpcyByZWdpb24gaW5jbHVkZXMgY291bnRyaWVzIGxpa2UgQ2hpbmEsIEF1c3RyYWxpYSwgSW5kb25lc2lhLCBUaGFpbGFuZCwgZXRjLg0KDQoyLiAgRXVyb3BlIGFuZCBDZW50cmFsIEFzaWE6IFRoaXMgcmVnaW9uIGluY2x1ZGVzIGNvdW50cmllcyBsaWtlIEdlcm1hbnksIFVLLCBSdXNzaWEsIFR1cmtleSwgZXRjLg0KDQozLiAgTGF0aW4gQW1lcmljYSAmIENhcmliYmVhbjogVGhpcyByZWdpb24gaW5jbHVkZXMgY291bnRyaWVzIGxpa2UgQnJhemlsLCBNZXhpY28sIEFyZ2VudGluYSwgQ3ViYSwgZXRjLg0KDQo0LiAgTWlkZGxlIEVhc3QgYW5kIE5vcnRoIEFmcmljYTogVGhpcyByZWdpb24gaW5jbHVkZXMgY291bnRyaWVzIGxpa2UgU2F1ZGkgQXJhYmlhLCBFZ3lwdCwgSXJhbiwgSXJhcSwgZXRjLg0KDQo1LiAgTm9ydGggQW1lcmljYTogVGhpcyBpcyBwcmVkb21pbmFudGx5IFVuaXRlZCBTdGF0ZXMgYW5kIENhbmFkYS4NCg0KNi4gIFNvdXRoIEFzaWE6IFRoaXMgcmVnaW9uIGluY2x1ZGVzIGNvdW50cmllcyBsaWtlIEluZGlhLCBQYWtpc3RhbiwgQmFuZ2xhZGVzaCwgU3JpIExhbmthLCBldGMuDQoNCjcuICBTdWItU2FoYXJhbiBBZnJpY2E6IFRoaXMgcmVnaW9uIGluY2x1ZGVzIGNvdW50cmllcyBsaWtlIE5pZ2VyaWEsIFNvdXRoIEFmcmljYSwgRXRoaW9waWEsIEtlbnlhLCBldGMuDQoNCk5vdGU6IFVNKFRoZSBVbml0ZWQgU3RhdGVzIE1pbm9yIE91dGx5aW5nIElzbGFuZHMpIGFuZCBBUShBbnRhcmN0aWNhKSBkb24ndCBiZWxvbmcgdG8gYW55IG9mIHRoZXNlIHJlZ2lvbnMsIHRodXMsIHRoZXkgd2lsbCBiZSB1c2VkIGFzIHRoZXkgYXJlLg0KDQpgYGB7cn0NCg0KDQp1bT13aGljaChkYXRhc2V0JGNvbXBhbnlfbG9jYXRpb249PSJVTSIpDQphcT13aGljaChkYXRhc2V0JGNvbXBhbnlfbG9jYXRpb249PSJBUSIpDQoNCg0KZGF0YXNldCRjb21wYW55X2xvY2F0aW9uIDwtIGNvdW50cnljb2RlKGRhdGFzZXQkY29tcGFueV9sb2NhdGlvbiwgImlzbzJjIiwgInJlZ2lvbiIpDQpkYXRhc2V0JGVtcGxveWVlX3Jlc2lkZW5jZSA8LSBjb3VudHJ5Y29kZShkYXRhc2V0JGVtcGxveWVlX3Jlc2lkZW5jZSwgImlzbzJjIiwgInJlZ2lvbiIpDQoNCmRhdGFzZXRbdW0sImNvbXBhbnlfbG9jYXRpb24iXT0iVU0iDQpkYXRhc2V0W2FxLCJjb21wYW55X2xvY2F0aW9uIl09IkFRIg0KDQpgYGANCg0KQ29uY2VwdCBoaWVyYXJjaHkgZ2VuZXJhdGlvbiBjYW4gYmUgZG9uZSBmb3IgImpvYl90aXRsZSIgYXMgd2VsbCB0byBpbXByb3ZlIGludGVycHJldGF0aW9uIGFuZCBzY2FsYWJpbGl0eS4gQWxzbywgbW9zdCBqb2IgdGl0bGVzIGFyZSBlc3NlbnRpYWxseSB0aGUgc2FtZSBqb2IgYnV0IHdpdGggZGlmZmVyZW50IG5hbWVzLCBzbyB3ZSBjYW4gY29tYmluZSB0aGVtIGludG8gYSBoaWdoZXItbGV2ZWwgam9icyB0aXRsZXMgc3VjaCBhcyBBcmNoaXRlY3QsIEFuYWx5c3QgYW5kIEVuZ2luZWVyIGV0Yy4NCg0KYGBge3J9DQojIyBDcmVhdGUgdGhlIGNhdGVnb3JpZXMgYmFzZWQgb24gam9iIHJhbmsgDQpkYXRhc2V0JGpvYl90aXRsZSA8LSBpZmVsc2UoZ3JlcGwoIkFuYWx5c3QiLCBkYXRhc2V0JGpvYl90aXRsZSksICJBbmFseXN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJBcmNoaXRlY3QiLCBkYXRhc2V0JGpvYl90aXRsZSksICJBcmNoaXRlY3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJFbmdpbmVlciIsIGRhdGFzZXQkam9iX3RpdGxlKSwgIkVuZ2luZWVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIk1hbmFnZXJ8T2ZmaWNlcnxEaXJlY3RvcnxMZWFkZXIiLCBkYXRhc2V0JGpvYl90aXRsZSksICJMZWFkZXJzaGlwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDb25zdWx0YW50fFNwZWNpYWxpc3QiLCBkYXRhc2V0JGpvYl90aXRsZSksICJDb25zdWx0YW50L1NwZWNpYWxpc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDeWJlciIsIGRhdGFzZXQkam9iX3RpdGxlKSwgIkN5YmVyIFNlY3VyaXR5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT3RoZXJzIikpKSkpKQ0KDQpgYGANCg0KIyMgRW5jb2RpbmcgY2F0ZWdvcmljYWwgZGF0YQ0KDQpUbyBkZWFsIHdpdGggY29sdW1ucyB3aXRoIGNoYXJhY3RlciB0eXBlIHdlIGFyZSBnb2luZyB0byBlbmNvZGUgdGhlbSwgYmVjYXVzZSBtb3N0IG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyBhcmUgZGVzaWduZWQgdG8gd29yayB3aXRoIGZhY3RvcnMgZGF0YSByYXRoZXIgdGhhbiBjaGFyYWN0ZXIgZGF0YSBhbmQgaXQgaW1wcm92ZXMgcGVyZm9ybWFuY2UgYW5kIEludGVycHJldGFiaWxpdHkgb2YgZGF0YSBhcyB3ZWxsLg0KDQpgYGB7cn0NCmRhdGFzZXQkam9iX3RpdGxlICA8LSBmYWN0b3IoZGF0YXNldCRqb2JfdGl0bGUpDQoNCmRhdGFzZXQkZXhwZXJpZW5jZV9sZXZlbCA9IGZhY3RvcihkYXRhc2V0JGV4cGVyaWVuY2VfbGV2ZWwsIGxldmVscz1jKCJFTiIsICJNSSIsICJTRSIsICJFWCIpLCBsYWJlbHM9YygxLDIsMyw0KSkNCg0KZGF0YXNldCRlbXBsb3ltZW50X3R5cGUgIDwtIGZhY3RvcihkYXRhc2V0JGVtcGxveW1lbnRfdHlwZSkNCg0KZGF0YXNldCRlbXBsb3llZV9yZXNpZGVuY2UgIDwtIGZhY3RvcihkYXRhc2V0JGVtcGxveWVlX3Jlc2lkZW5jZSkNCg0KZGF0YXNldCRjb21wYW55X2xvY2F0aW9uICA8LSBmYWN0b3IoZGF0YXNldCRjb21wYW55X2xvY2F0aW9uKQ0KDQpkYXRhc2V0JHNhbGFyeV9jdXJyZW5jeSAgPC0gZmFjdG9yKGRhdGFzZXQkc2FsYXJ5X2N1cnJlbmN5KQ0KDQpkYXRhc2V0JGpvYl90aXRsZSAgPC0gZmFjdG9yKGRhdGFzZXQkam9iX3RpdGxlKQ0KDQoNCmRhdGFzZXQkY29tcGFueV9zaXplID0gZmFjdG9yKGRhdGFzZXQkY29tcGFueV9zaXplLCBsZXZlbHM9YygiUyIsIk0iLCJMIiksIGxhYmVscz1jKDEsMiwzKSkNCg0KDQpkYXRhc2V0JGpvYl90aXRsZSAgPC0gZmFjdG9yKGRhdGFzZXQkam9iX3RpdGxlKQ0KDQpgYGANCg0KIyMjIERpc2NyZXRpemF0aW9uIG9mIHNhbGFyYXlfaW5fdXNkIGF0dHJpYnV0ZQ0KDQpieSBjYWxjdWxhdGluZyBicmVha3MgYmFzZWQgb24gcXVhcnRpbGVzDQoNCmBgYHtyfQ0KYnJlYWtzIDwtIHF1YW50aWxlKGRhdGFzZXQkc2FsYXJ5X2luX3VzZCwgDQogICAgICAgICAgICAgICAgICAgcHJvYnMgPSBjKDAsIC4yNSwgLjUsIC43NSwgLjk1LCAxKSwgDQogICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKQ0KDQoNCmRhdGFzZXQkc2FsYXJ5X2luX3VzZCA8LSBjdXQoZGF0YXNldCRzYWxhcnlfaW5fdXNkLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGJyZWFrcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlZlcnkgTG93IiwgIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkNCg0KDQpgYGANCg0KIyMjIE5vcm1hbGl6YXRpb246DQoNCnRvIGNoYW5nZSB0aGUgc2NhbGUgb2YgbnVtZXJpYyBhdHRyaWJ1dGVzIChyZW1vdGVfcmF0aW8gYW5kIHdvcmtfeWVhcikgdG8gYSBzY2FsZSBvZiBbLTEsMV0gdG8gZ2l2ZSB0aGVtIGVxdWFsIHdlaWdodA0KDQpgYGB7cn0NCmRhdGFzZXQgWywgYygid29ya195ZWFyIiAsICJyZW1vdGVfcmF0aW8iKV0gPSBzY2FsZShkYXRhc2V0IFssIGMoIndvcmtfeWVhciIgLCAicmVtb3RlX3JhdGlvIildKQ0KYGBgDQoNCiMjIEZlYXR1cmUgU2VsZWN0aW9uDQoNCndlIHdpbGwgaW1wbGVtZW50IGZlYXR1cmUgc2VsZWN0aW9uIHRvIHJlbW92ZSByZWR1bmRhbnQgb3IgaXJyZWxldmFudCBhdHRyaWJ1dGVzIGZyb20gdGhlIGRhdGEgc2V0IHRvIGdldCB0aGUgc21hbGxlc3Qgc3Vic2V0IHRoYXQgY2FuIGhlbHAgdXMgZ2V0IHRoZSBtb3N0IGFjY3VyYXRlIHByZWRpY3Rpb25zIGZvciBvdXIgdGFyZ2V0IGNsYXNzKHNhbGFyeV9pbl91c2QpIGFuZCBkZWNyZWFzZSB0aGUgdGltZSB0aGF0IGl0IHRha2VzIHRoZSBjbGFzc2lmaWVyIHRvIHByb2Nlc3MgdGhlIGRhdGEuDQoNCndlIHdpbGwgdXNlIFJGRShSZWN1cnNpdmUgZmVhdHVyZSBlbGltaW5hdGlvbikgd2hpY2ggaXMgYSB3cmFwcGVyIG1ldGhvZCBmb3IgdGhlIGZlYXR1cmUgc2VsZWN0aW9uLiBTaW5jZSB0aGUgUkZFIGZ1bmN0aW9uIGhhdmUgbXVsdGlwbGUgY29udHJvbCBvcHRpb25zIHdlIG5lZWQgdG8gc3BlY2lmeSB0aGUgb3B0aW9ucyB0aGF0IHdlIHdhbnQuIFdlIHdpbGwgY2hvb3NlICJSYW5kb20gRm9yZXN0IiBiZWNhdXNlIGl0IGhhcyBoaWdoIGFjY3VyYWN5LCBjYW4gaGFuZGxlIGNhdGVnb3JpY2FsIGRhdGEuDQoNCmBgYHtyfQ0KY29udHJvbCA8LSByZmVDb250cm9sKGZ1bmN0aW9ucyA9IHJmRnVuY3MsIA0KICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwNCiAgICAgICAgICAgICAgICAgICAgICByZXBlYXRzID0gNSwgDQogICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTApDQpgYGANCg0KRmlyc3Qgd2Ugc2F2ZSB0aGUgZmVhdHVyZXMgdG8gYmUgdXNlZCBpbiB0aGUgZmVhdHVyZSBzZWxlY3Rpb24oZXZlcnkgYXR0cmlidXRlcyBleGNlcHQgdGhlIGNsYXNzIGxhYmVsICJzYWxhcnlfaW5fdXNkIikgaW4gdmFyaWFibGUgeCwgYW5kIHRoZSBjbGFzcyBsYWJlbCBpbiB2YXJpYWJsZSB5LiBUaGVuIHNwbGl0IHRoZSBkYXRhIHRvIDgwJSB0cmFpbmluZyBhbmQgMjAlIHRlc3QuDQoNCmBgYHtyfQ0KeCA8LSBkYXRhc2V0ICU+JQ0KICBzZWxlY3QoLXNhbGFyeV9pbl91c2QpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkNCg0KIyBUYXJnZXQgdmFyaWFibGUNCnkgPC0gZGF0YXNldCRzYWxhcnlfaW5fdXNkDQoNCiMgVHJhaW5pbmc6IDgwJTsgVGVzdDogMjAlDQpzZXQuc2VlZCgyMDIxKQ0KaW5UcmFpbiA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHksIHAgPSAuODAsIGxpc3QgPSBGQUxTRSlbLDFdDQoNCnhfdHJhaW4gPC0geFsgaW5UcmFpbiwgXQ0KeF90ZXN0ICA8LSB4Wy1pblRyYWluLCBdDQoNCnlfdHJhaW4gPC0geVsgaW5UcmFpbl0NCnlfdGVzdCAgPC0geVstaW5UcmFpbl0NCg0KYGBgDQoNCmFmdGVyIHNwbGl0dGluZyB0aGUgZGF0YSwgbm93IHdlIGNhbiBwZXJmb3JtIHRoZSBzZWxlY3Rpb24gdXNpbmcgcmZlDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCnJlc3VsdF9yZmUxIDwtIHJmZSh4ID0geF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgeSA9IHlfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgIHNpemVzID0gYygxOjkpLA0KICAgICAgICAgICAgICAgICAgIHJmZUNvbnRyb2wgPSBjb250cm9sKQ0KDQpyZXN1bHRfcmZlMQ0KDQpwcmVkaWN0b3JzKHJlc3VsdF9yZmUxKQ0KDQpgYGANCg0KVGhlIHJlc3VsdHMgc2hvdyB0aGF0IGFsbCB0aGUgcmVtYWluaW5nIGF0dHJpYnV0ZXMsIGV4Y2VwdCBmb3IgImVtcGxveW1lbnRfdHlwZSIsIGFyZSBzZWxlY3RlZC4gVGhpcyBpcyBsb2dpY2FsLCBhcyA5OCUgb2YgdGhlIHJvd3MgaGF2ZSB0aGUgdmFsdWUgIkZUIiwgYXMgc2hvd24gaW4gdGhlIHRhYmxlIGJlbG93LiBEdWUgdG8gdGhlIGxvdyB2YXJpYW5jZSwgd2UgZGVjaWRlZCB0byByZW1vdmUgdGhpcyBhdHRyaWJ1dGUuDQoNCmBgYHtyfQ0KdGFibGUoZGF0YXNldCRlbXBsb3ltZW50X3R5cGUpDQpgYGANCg0KYGBge3J9DQpkYXRhc2V0PC1kYXRhc2V0Wywtd2hpY2gobmFtZXMoZGF0YXNldCk9PSJlbXBsb3ltZW50X3R5cGUiKV0NCmBgYA0KDQojIHBoYXNlIDMNCg0KRHVyaW5nIHRoaXMgcGhhc2UsIG91ciBmb2N1cyB3aWxsIGJlIG9uIGNsdXN0ZXJpbmcgYW5kIGNsYXNzaWZpY2F0aW9uIHRlY2huaXF1ZXMgdG8gYW5hbHl6ZSB0aGUgZGF0YS4gVGhlIHByaW1hcnkgb2JqZWN0aXZlcyBhcmUgdG8gaWRlbnRpZnkgZGlzdGluY3QgZ3JvdXBzIHdpdGhpbiB0aGUgZGF0YXNldCB0aHJvdWdoIGNsdXN0ZXJpbmcsIGNsYXNzaWZ5IGRhdGEgb2JqZWN0cyBpbnRvIG1lYW5pbmdmdWwgY2F0ZWdvcmllcywgYW5kIGFwcGx5IGRpZmZlcmVudCBldmFsdWF0aW9uIG1ldGhvZHMgdG8gYXNzZXNzIHRoZSBhY2N1cmFjeSBhbmQgcHJlY2lzaW9uIG9mIGJvdGggY2xhc3NpZmljYXRpb24gYW5kIGNsdXN0ZXJpbmcgcmVzdWx0cy4gV2UgYWltIHRvIGdhaW4gZGVlcGVyIGluc2lnaHRzIGludG8gdGhlIGRhdGEgYW5kIGRpc2NvdmVyIHBhdHRlcm5zLg0KDQojIyBSZXRyZWl2ZSBvdXIgcHJlcHJvY2Vzc2VkIGRhdGFzZXQNCg0KYGBge3J9DQoNCiMgUmVhZCB0aGUgQ1NWIGZpbGUgZnJvbSBnaXRodWINCmRhdGFzZXQyPSByZWFkLmNzdih1cmwoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9TYXJhaEFsaGluZGkvRE1fcHJvamVjdC9tYWluL0RhdGElMjBTZXQvcHJlcHJvY2Vzc2VkRGF0YXNldC5jc3YiKSwgaGVhZGVyPVRSVUUpDQoNCiMgSWRlbnRpZnkgdGhlIGNoYXJhY3RlciB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQyDQpjaGFyX3ZhcnMgPC0gc2FwcGx5KGRhdGFzZXQyLCBpcy5jaGFyYWN0ZXIpDQoNCiMgQ29udmVydCB0aGUgaWRlbnRpZmllZCBjaGFyYWN0ZXIgdmFyaWFibGVzIGluIGRhdGFzZXQyIHRvIGZhY3RvcnMNCmRhdGFzZXQyW2NoYXJfdmFyc10gPC0gbGFwcGx5KGRhdGFzZXQyW2NoYXJfdmFyc10sIGFzLmZhY3RvcikNCg0KYGBgDQoNCiMjIGJhbGFuY2luZyBkYXRhDQoNClRvIHJlc29sdmUgdGhlIHByb2JsZW0gb2YgY2xhc3MgaW1iYWxhbmNlIGluIHRoZSBkYXRhc2V0LCB3ZSB3aWxsIHVzZSBTTU9URSgpIG1ldGhvZCB0aGF0IG92ZXJzYW1wbGUgdGhlIG1pbm9yaXR5IGNsYXNzIGJ5IGNyZWF0aW5nIHN5bnRoZXRpYyBzYW1wbGVzIHVzaW5nIHRoZSBleGlzdGluZyBtaW5vcml0eSBjbGFzcyBzYW1wbGVzDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTApDQpiYWxhbmNlZF9kYXRhc2V0IDwtIFNNT1RFKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhc2V0MiwgcGVyYy5vdmVyID0gMzAwLCBwZXJjLnVuZGVyPTUwMCwgayA9IDEwKQ0KYGBgDQoNCg0KIyMgRGF0YSBtaW5pbmcgdGVjaG5pcXVlcw0KDQoNClRoZSBnb2FsIG9mIGFsbCBwcmVjZWRpbmcgc3RlcHMgaXMgdG8gcHJvcGVybHkgcHJlcGFyZSB0aGUgZGF0YXNldCBmb3IgdGhlIGNsYXNzaWZpY2F0aW9uIGFuZCBjbHVzdGVyaW5nLCB3aGljaCBjb25zdGl0dXRlcyBvbmUgb2Ygb3VyIHByaW1hcnkgbWluaW5nIG9iamVjdGl2ZXMuIEluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBlbXBsb3kgdmFyaW91cyBhdHRyaWJ1dGUgc2VsZWN0aW9uIG1ldGhvZHMgc3VjaCBhcyB0aGUgR2luaSBpbmRleCwgR2FpbiByYXRpbywgYW5kIGluZm9ybWF0aW9uIGdhaW4gdG8gY29uc3RydWN0IGEgZGVjaXNpb24gdHJlZSBtb2RlbC4gV2Ugd2lsbCB0aG9yb3VnaGx5IGV2YWx1YXRlIGl0cyBwZXJmb3JtYW5jZSwgYW5kIGlmIGl0IHByb3ZlcyBlZmZlY3RpdmUsIGl0IGNhbiBzdWJzZXF1ZW50bHkgYmUgdXRpbGl6ZWQgdG8gY2xhc3NpZnkgbmV3IGluc3RhbmNlcyB3aXRoIHVua25vd24gY2xhc3MgbGFiZWxzLiBUaGUgcHJvY2VzcyB0byBwcmVkaWN0IGlzIGFzIGZvbGxvdywgZGl2aWRlIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIGRhdGEgc2V0cyB0aGVuIHRyYWluaW5nIHRoZSBtb2RlbCB1c2luZyB0aGUgdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IGl0cyBwZXJmb3JtYW5jZSB1c2luZyB0aGUgdGVzdCBzZXQuDQoNCnNpbmNlIG91ciBkYXRhc2V0IGlzIHNtYWxsLCB3ZSBkZWNpZGVkIHRvIHVzZSBLLWZvbGQgQ3Jvc3MtdmFsaWRhdGlvbiBhcyBhIGRhdGFzZXQgcGFydGlvbmluZyBtZXRob2QuIGZvciBlYWNoIGF0dHJpYnV0ZSBzZWxlY3Rpb24gbWV0aG9kIHdlIHdpbGwgdHJ5IGRpZmZlcmVudCBLIHNpemUgKDEwLDUsIGFuZCAzKQ0KDQppbiBhbGwgdGhpcyBzZWN0aW9uIHdlIHdpbGwgYmUgdXNpbmcgdHJhaW4gYW5kIHRyYWluQ29udHJvbCBmdW5jdGlvbnMgb2YgY2FyZXQgcGFja2FnZSB0byBwcm9kdWNlIGRlY2lzaW9uIHRyZWVzLiBmb3IgR2luaSBpbmRleCB0aGUgbWV0aG9kIHdpbGwgYmUgInJwYXJ04oCdIGZyb20gInJwYXJ0IiAgcGFja2FnZSBhbmQgZm9yIEdhaW4gcmF0aW8gaXQncyAiajQ4IiBmcm9tICJSV2VrYSIgcGFja2FnZSBhcyBmb3IgaW5mb3JtYXRpb24gZ2FpbiB0aGUgbWV0aG9kIGlzICJDNS4wIiBmcm9tICJDNTAiIHBhY2thZ2UgIC4NCg0KDQoNCkRhdGEgY2x1c3RlcmluZyBpcyBhIHByb2Nlc3MgdG8gcGFydGl0aW9uIGRhdGEgaW50byBncm91cHMgb3IgY2x1c3RlcnMsaXQgaXMgYW4gdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHByb2Nlc3MsIHdoaWNoIGlzIGV4Y3V0ZWQgd2l0aG91dCBrbm93aW5nIHRoZSBjbGFzcyBsYWJlbCBvZiB0aGUgdHJhaW5pbmcgZGF0YS4gVGhlIGRhdGEgaW4gdGhlIHNhbWUgZ3JvdXAgImNsdXN0ZXIiIGFyZSBzaW1pbGFyIHRvIG9uZSBhbm90aGVyIGFuZCBkaWZmZXJlbnQgZnJvbSBkYXRhIGluIG90aGVyIGNsdXN0ZXJzLiBBbmQgZm9yIHRoaXMgZGF0YSBtaW5pbmcgdGFzayBXZSB3aWxsIHV0aWxpemUgay1tZWFucyBjbHVzdGVyaW5nLiANCldlIHdpbGwgdXNlIHRoZSBtZXRob2QgImZ2aXpfbmJjbHVzdCIgIG9mIHRoZSBwYWNrYWdlICJmYWN0b2V4dHJhIiB0byBmaW5kIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgYmFzZWQgb24gdGhlIGVsYm93IG1ldGhvZCBhbmQgdGhlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQuIFRvIHVzZSB0aGUga21lYW5zIGNsdXN0ZXJpbmcgd2Ugd2lsbCB1dGlsaXplIHRoZSBtZXRob2Qg4oCca21lYW5z4oCdIG9mIHRoZSBwYWNrYWdlIOKAnHN0YXRz4oCdLiBUbyB2aXN1YWxpemUgdGhlIGNsdXN0ZXJzLCB3ZSB3aWxsIHVzZSB0aGUgbWV0aG9kIOKAnGZ2aXpfY2x1c3RlcuKAnSBmcm9tIHRoZSBwYWNrYWdlIOKAnGZhY3RvZXh0cmHigJ0uIEFuZCBmaW5hbGx5IHRvIGZpbmQgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSBmb3IgZWFjaCBjbHVzdGVyIHRoZSBtZXRob2Qg4oCcc2lsaG91ZXR0ZeKAnSBmcm9tIHRoZSBwYWNrYWdlIOKAnGNsdXN0ZXLigJ0gd2lsbCBiZSB1c2VkLiANCg0KDQojIyBFdmFsdWF0aW9uIGFuZCBDb21wYXJpc29uDQoNCg0KDQojIyMgQ2xhc3NpZmljYXRpb24NCg0KDQoNCnRoZSBmb2xsb3dpbmcgZnVuY3Rpb24gd2lsbCBiZSB1c2VkIHRvIGNvbXB1dGUgYXZlcmFnZSBzZW5zaXRpdml0eSBhbmQgU3BlY2lmaWNpdHk6DQoNCmBgYHtyfQ0KDQoNCm1hY3JvID0gZnVuY3Rpb24obWF0cml4KXsNCiAgDQogIHN1bVNlbj0wDQogIA0KICBmb3IgKGkgaW4gMTo1KSB7DQogICBzdW1TZW4gPSBzdW1TZW4gKyBtYXRyaXgkYnlDbGFzc1tpLDFdIA0KICB9DQogIA0KICANCiAgYXZnU2VuID0gc3VtU2VuLzUNCiAgDQogIHN1bVNwZWM9MA0KICANCiAgZm9yIChpIGluIDE6NSkgew0KICAgc3VtU3BlYyA9IHN1bVNwZWMgKyBtYXRyaXgkYnlDbGFzc1tpLDJdIA0KICB9DQogIGF2Z1NwZWMgPSBzdW1TcGVjLzUNCiAgDQogIA0KICANCiAgDQogIHN1bVByZWM9MA0KICANCiAgZm9yIChpIGluIDE6NSkgew0KICAgc3VtUHJlYyA9IHN1bVByZWMgKyBtYXRyaXgkYnlDbGFzc1tpLDNdIA0KICB9DQogIGF2Z1ByZWMgPSBzdW1QcmVjLzUNCiAgDQogIA0KICANCiAgDQogIGF2Z3MgPSBkYXRhLmZyYW1lKFNlbnNpdGl2aXR5PWF2Z1NlbiAsIFNwZWNpZmljaXR5PWF2Z1NwZWMsIFByZWNpc2lvbj1hdmdQcmVjICxBY2N1cmFjeT0gdW5uYW1lKCBtYXRyaXgkb3ZlcmFsbFsxXSkgKQ0KICBwcmludChhdmdzKQ0KICANCiAgDQp9DQoNCg0KYGBgDQoNCiMjIyMgR2luaSBpbmRleA0KDQpHaW5pIGluZGV4IG1lYXN1cmVzIHRoZSBpbXB1cml0eSBvZiB0aGUgZGF0YXNldC4gVGhlIHBhcnRpdGlvbmluZyB0aGF0IHlpZWxkcyB0aGUgbW9zdCBzdWJzdGFudGlhbCByZWR1Y3Rpb24gaW4gaW1wdXJpdHkgaXMgc2VsZWN0ZWQgYXMgdGhlIHNwbGl0dGluZyBhdHRyaWJ1dGUuIFRvIGFwcGx5IHRoZSBHaW5pIGluZGV4LCB3ZSB3aWxsIGVtcGxveSB0aGUgInJwYXJ0IiBtZXRob2QsIHdoaWNoIHV0aWxpemVzIHRoZSBHaW5pIGluZGV4IGFzIHRoZSBjcml0ZXJpYSBmb3Igc3BsaXR0aW5nLg0KDQojIyMjIyAxMCBGb2xkcw0KDQpUaGUgdHJlZSBvZiB0aGUgZ2luaSBpbmRleCB1c2luZyAxMCBmb2xkcw0KDQpgYGB7cn0NCg0Kc2V0LnNlZWQoMTApDQpjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCwgcmV0dXJuUmVzYW1wPSJhbGwiLCBzYXZlUHJlZGljdGlvbnM9ImZpbmFsIikNCg0KdHVuZUdyaWQgPC0gZXhwYW5kLmdyaWQoY3AgPSBjKDAuMDAxLCAwLjAwNSwgMC4wMSkpDQoNCmdpbmlJbmRleDEwIDwtIHRyYWluKA0KICBzYWxhcnlfaW5fdXNkIH4gLiwNCiAgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsDQogIG1ldGhvZCA9ICJycGFydCIsDQogIHRyQ29udHJvbCA9IGN0cmwsdHVuZUdyaWQ9dHVuZUdyaWQsDQogIGNvbnRyb2wgPSBsaXN0KA0KICAgIG1pbnNwbGl0ID0gMTAsDQogICAgbWluYnVja2V0ID0gNSwNCiAgICB4dmFsID0gMTAsDQogICAgY3AgPSAwLjAwMDENCiAgKQ0KDQopDQoNCg0KcHJwKGdpbmlJbmRleDEwJGZpbmFsTW9kZWwsIGJveC5wYWxldHRlID0gIlJlZHMiLCB0d2VhayA9IDEuMiwgdmFybGVuID0gMjApDQoNCmBgYA0KDQp0aGUgImV4cGVyaW5jZSBsZXZlbCIgYXR0cmlidXRlIHdhcyBzZWxlY3RlZCBhcyB0aGUgZmlyc3Qgc3BsaXR0aW5nIGF0dHJpYnV0ZSBtZWFuaW5nIHRoYXQgaXQgaGFzIHRoZSBsYXJnZXN0IGltcHVyaXR5IHJlZHVjdGlvbi4NCg0KIyMjIyMjIENvbmZ1c2lvbiBtYXRyaXggb2YgMTAgZm9sZHMgdXNpbmcgR2luaSBJbmRleA0KDQpUaGUgZm9sbG93aW5nIGNvbmZ1c2lvbiBNYXRyaXggd2lsbCBzaG93IHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgY2xhc3NpZmllciB1c2luZyB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscyBhbmQgdGhlIGFjdHVhbCBjbGFzcyBsYWJlbHMgb2Ygb3VyIGRhdGFzZXQNCg0KYGBge3J9DQoNCmdpbmlJbmRleDEwY20gPSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGdpbmlJbmRleDEwJHByZWQkb2JzLGdpbmlJbmRleDEwJHByZWQkcHJlZCkNCg0KZ2luaUluZGV4MTBjbQ0KDQpgYGANCg0KdGhlIG1ldHJpY3Mgc2hvd24gZm9yIGVhY2ggY2xhc3MgaW5kaWNhdGUgdGhlIHZhbHVlIG9mIHRoYXQgbWV0cmljIHdoZW4gdHJlYXRpbmcgdGhpcyBjbGFzcyBhcyB0aGUgcG9zaXRpdmUgY2xhc3MgYW5kIHRoZSBvdGhlciBjbGFzc2VzIGFzIHRoZSBuZWdhdGl2ZSBjbGFzcy4gaGVyZSB0aGUgY2xhc3NpZmllciBzaG93ZWQgYmVzdCBwZXJmb3JtYW5jZSB3aGVuIHVzaW5nIHRoZSAidmVyeSBoaWdoIiBjbGFzcyBhcyB0aGUgcG9zaXRpdmUgY2xhc3MgYnV0IHRoaXMgdmFsdWUgaW4gaXRzIG93biBkb2Vzbid0IGhvbGQgbXVjaCB2YWx1ZSBzaW5jZSBhbGwgY2xhc3NlcyBzaG91bGQgYmUgdGFrZW4gaW50byBjb25zaWRlcmF0aW9uLg0KDQojIyMjIyA1IEZvbGRzDQoNClRoZSB0cmVlIG9mIHRoZSBnaW5pIGluZGV4IHVzaW5nIDUgZm9sZHMNCg0KYGBge3J9DQpzZXQuc2VlZCgxMCkNCmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDUsIHJldHVyblJlc2FtcD0iYWxsIiwgc2F2ZVByZWRpY3Rpb25zPSJmaW5hbCIpDQoNCg0KdHVuZUdyaWQgPC0gZXhwYW5kLmdyaWQoY3AgPSBjKDAuMDAxLCAwLjAwNSwgMC4wMSkpDQoNCmdpbmlJbmRleDUgPC0gdHJhaW4oc2FsYXJ5X2luX3VzZCB+IC4sIGRhdGEgPSBiYWxhbmNlZF9kYXRhc2V0LCBtZXRob2QgPSAicnBhcnQiLCB0ckNvbnRyb2wgPSBjdHJsLHR1bmVHcmlkPXR1bmVHcmlkLA0KICBjb250cm9sID0gbGlzdCgNCiAgICBtaW5zcGxpdCA9IDEwLA0KICAgIG1pbmJ1Y2tldCA9IDUsDQogICAgeHZhbCA9IDEwLA0KICAgIGNwID0gMC4wMDAxDQogICkpDQoNCnBycChnaW5pSW5kZXg1JGZpbmFsTW9kZWwsIGJveC5wYWxldHRlID0gIlJlZHMiLCB0d2VhayA9IDEuNSwgdmFybGVuID0gMTAsIGNleCA9IDAuMTUpDQoNCg0KYGBgDQoNCnRoaXMgdHJlZSBoYXMgdGhlIHNhbWUgc3RydWN0dXJlIGFzIHRoZSBwcmV2aW91cyB0cmVlIHRoYXQgdXNlZCAxMCBmb2xkcy4gc28gaW4gdGhpcyB0cmVlIGFzIHdlbGwgImV4cGVyaWVuY2UgbGV2ZWwiIHdhcyBjaG9vc2UgYXMgdGhlIGZpcnN0IHNwbGl0dGluZyBhdHRyaWJ1dGUNCg0KIyMjIyMjIENvbmZ1c2lvbiBtYXRyaXggb2YgNSBmb2xkcyB1c2luZyBHaW5pIEluZGV4DQoNClRoZSBmb2xsb3dpbmcgY29uZnVzaW9uIE1hdHJpeCB3aWxsIHNob3cgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBjbGFzc2lmaWVyIHVzaW5nIHRoZSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWxzIGFuZCB0aGUgYWN0dWFsIGNsYXNzIGxhYmVscyBvZiBvdXIgZGF0YXNldA0KDQpgYGB7cn0NCmdpbmlJbmRleDVjbSA9IGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZ2luaUluZGV4NSRwcmVkJG9icyxnaW5pSW5kZXg1JHByZWQkcHJlZCkNCg0KZ2luaUluZGV4NWNtDQoNCmBgYA0KDQp0aGUgcmVzdWx0cyBhcmUgdmVyeSBjbG9zZSB0byB0aGUgMTAgZm9sZHMgdHJlZSwgc28gaGVyZSBhcyB3ZWxsIHRoZSBjbGFzc2lmaWVyIHNob3dzIGJldHRlciBwZXJmb3JtYW5jZSB3aGVuIGRlYWxpbmcgd2l0aCB0aGUgInZlcnkgaGlnaCINCg0KIyMjIyMgMyBGb2xkcw0KDQpUaGUgdHJlZSBvZiB0aGUgZ2luaSBpbmRleCB1c2luZyAzIGZvbGRzDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTApDQpjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAzLCByZXR1cm5SZXNhbXA9ImFsbCIsIHNhdmVQcmVkaWN0aW9ucz0iZmluYWwiKQ0KDQoNCnR1bmVHcmlkIDwtIGV4cGFuZC5ncmlkKGNwID0gYygwLjAwMSwgMC4wMDUsIDAuMDEpKQ0KDQpnaW5pSW5kZXgzIDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gInJwYXJ0IiwgdHJDb250cm9sID0gY3RybCx0dW5lR3JpZD10dW5lR3JpZCwNCiAgY29udHJvbCA9IGxpc3QoDQogICAgbWluc3BsaXQgPSAxMCwNCiAgICBtaW5idWNrZXQgPSA1LA0KICAgIHh2YWwgPSAxMCwNCiAgICBjcCA9IDAuMDAwMQ0KICApKQ0KDQpwcnAoZ2luaUluZGV4MyRmaW5hbE1vZGVsLCBib3gucGFsZXR0ZSA9ICJSZWRzIiwgdHdlYWsgPSAxLjUsIHZhcmxlbiA9IDEwLCBjZXggPSAwLjE1KQ0KDQoNCmBgYA0KDQpUaGUgdHJlZSBzaG93cyBzaW1pbGFyIHN0cnVjdHVyZSBhcyB0aGUgdHdvIHByZXZpb3VzIHR3byB0cmVlcywgd2hldGhlciBpdCdzIGluIGl0cyBjaG9vc2Ugb2YgdGhlIHNwbGl0dGluZyBhdHRyaWJ1dGVzIG9yIHRoZSBsZWF2ZXMuDQoNCiMjIyMjIyBDb25mdXNpb24gbWF0cml4IG9mIDMgZm9sZHMgdXNpbmcgR2luaSBJbmRleA0KDQpUaGUgZm9sbG93aW5nIGNvbmZ1c2lvbiBNYXRyaXggd2lsbCBzaG93IHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgY2xhc3NpZmllciB1c2luZyB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscyBhbmQgdGhlIGFjdHVhbCBjbGFzcyBsYWJlbHMgb2Ygb3VyIGRhdGFzZXQNCg0KYGBge3J9DQoNCmdpbmlJbmRleDNjbSA9IGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZ2luaUluZGV4MyRwcmVkJG9icyxnaW5pSW5kZXgzJHByZWQkcHJlZCkNCg0KZ2luaUluZGV4M2NtDQoNCmBgYA0KDQpoZXJlIGFzIHdlbGwgdGhlICJ2ZXJ5IGhpZ2giIGNsYXNzIGhhcyB0aGUgYmVzdCBvdmVyYWxsIHBlcmZvcm1hbmNlDQoNCiMjIyMjIyBBbmFseXNpcyBvZiB0aGUgZ2luaSBpbmRleCBjbGFzc2lmaWNhdGlvbg0KDQpBbGwgdGhyZWUgdHJlZXMgc2VlbSB0byBiZSBhbGlrZSBpbiB0aGVpciBhcnJhbmdlbWVudCBhbmQgZm9ybS4NCg0KMS4gIFJvb3QgTm9kZSAtIEV4cGVyaWVuY2UgTGV2ZWw6IFRoZSBpbml0aWFsIGF0dHJpYnV0ZSB1c2VkIGZvciBzcGxpdHRpbmcgdGhlIGRhdGFzZXQgYXQgdGhlIHJvb3Qgbm9kZSBpcyB0aGUgImV4cGVyaWVuY2UgbGV2ZWwuIiBUaGlzIGRpdmlkZXMgdGhlIHRyZWUgaW50byB0d28gbWFpbiBicmFuY2hlcyBvciBzdWJ0cmVlczoNCiAgICAtICAgUmlnaHQgU3VidHJlZTogVGhpcyBjb21wcmlzZXMgaW5zdGFuY2VzIHdpdGggU2VuaW9yIChTRSkgYW5kIEV4ZWN1dGl2ZSAoRVgpIGV4cGVyaWVuY2UgbGV2ZWxzLg0KICAgIC0gICBMZWZ0IFN1YnRyZWU6IFRoaXMgaW5jbHVkZXMgaW5kaXZpZHVhbHMgd2l0aCBFbnRyeSAoRU4pIGFuZCBNaWQgKE1JKSBleHBlcmllbmNlIGxldmVscy4NCjIuICBSaWdodCBTdWJ0cmVlIC0gd29yayB5ZWFyOiBUaGUgbmV4dCBhdHRyaWJ1dGUgdXNlZCB0byBmdXJ0aGVyIGNsYXNzaWZ5IHRoZSByaWdodCBzdWJ0cmVlIGlzICJ3b3JrIHllYXIuIiBUaGUgZGVjaXNpb24gY3JpdGVyaW9uIGlzOg0KICAgIC0gICBJZiB3b3JrIHllYXIgaXMgXDwtMS44OiBUaGVuIGl0IGlzIGhpZ2guDQogICAgLSAgIElmIHdvcmsgeWVhciBpcyBOT1QgXDwtMS44OiBUaGUgbmV4dCBhdHRyaWJ1dGUgZXhhbWluZWQgaXMgImV4cGVyaWVuY2UgbGV2ZWwuIg0KMy4gIExlZnQgU3VidHJlZSAtIEV4cGVyaWVuY2UgTGV2ZWw6IE9uIHRoZSBsZWZ0IHNpZGUgb2YgdGhlIHRyZWUsIHRoZSBhdHRyaWJ1dGUgImV4cGVyaWVuY2UgbGV2ZWwuIiBpcyB1c2VkIHRvIGZ1cnRoZXIgYmlmdXJjYXRlIHRoZSBpbnN0YW5jZXM6DQogICAgLSAgIElmIGV4cGVyaWVuY2UgbGV2ZWwgaXMgXD49MjogVGhlIG5leHQgYXR0cmlidXRlIGV4YW1pbmVkIGlzICJleHBlcmllbmNlIGxldmVsLiINCiAgICAtICAgSWYgZXhwZXJpZW5jZSBsZXZlbCBpcyBOT1QgXD49MjogVGhlIG5leHQgYXR0cmlidXRlIGFsc28gd2lsbCBleGFtaW5lZCBpcyAiZXhwZXJpZW5jZSBsZXZlbC4iDQoNCiMjIyMjIyBTZW5zaXRpdml0eSwgQWNjdXJhY3ksIFNwZWNpZml0eSBhbmQgcHJlY2lzaW9uIG9mIGFsbCAzLDUgYW5kIDEwIGZvbGRzIHVzaW5nIEdpbmkgSW5kZXgNCg0KYGBge3J9DQpyYmluZCgiMTAgRm9sZHMiPW1hY3JvKGdpbmlJbmRleDEwY20pLCAiNSBGb2xkcyI9bWFjcm8oZ2luaUluZGV4NWNtKSwgIjMgRm9sZHMiPW1hY3JvKGdpbmlJbmRleDNjbSkgICkgDQpgYGANCg0KVGhlIGhpZ2hlciB2YWx1ZXMgZm9yIHNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eSwgcHJlY2lzaW9uLCBhbmQgYWNjdXJhY3kgaW4gdGhlIDEwLWZvbGQgY2FzZSBpbmRpY2F0ZSBiZXR0ZXIgb3ZlcmFsbCBwZXJmb3JtYW5jZSBhY2NvcmRpbmcgdG8gdGhlc2UgbWV0cmljcy4gc28sR2luaSBJbmRleCBtb2RlbCBwZXJmb3JtcyBiZXR0ZXIgd2l0aCBhIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBjb21wYXJlZCB0byA1IGFuZCAzIGZvbGRzLg0KDQojIyMjIEdhaW4gcmF0aW8NCg0KVGhlIGdhaW4gcmF0aW8sIGEgbm9ybWFsaXplZCBtZWFzdXJlIG9mIGluZm9ybWF0aW9uIGdhaW4sIGlzIGNhbGN1bGF0ZWQgYnkgZGl2aWRpbmcgaW5mb3JtYXRpb24gZ2FpbiBieSB0aGUgc3BsaXQgaW5mb3JtYXRpb24uIFRoZSBhdHRyaWJ1dGUgdGhhdCB5aWVsZHMgdGhlIGhpZ2hlc3QgZ2FpbiByYXRpbyBpcyBjaG9zZW4gYXMgdGhlIHNwbGl0dGluZyBhdHRyaWJ1dGUuIFRoZSBDNC41IGFsZ29yaXRobSBlbXBsb3lzIHRoZSBnYWluIHJhdGlvLg0KDQpUaGUgSjQ4IGlzIHRoZSBKYXZhLWJhc2VkIG9wZW4tc291cmNlIGltcGxlbWVudGF0aW9uIG9mIHRoZSBDNC41IGFsZ29yaXRobSwgYW5kIGl0IGlzIGluY2x1ZGVkIGluIHRoZSBXZWthIHBhY2thZ2UuIFRoaXMgaW1wbGVtZW50YXRpb24gYWxsb3dzIHVzZXJzIHRvIGNvbnZlbmllbnRseSBhcHBseSB0aGUgQzQuNSBkZWNpc2lvbiB0cmVlLg0KDQojIyMjIyAxMCBGb2xkcw0KDQpUaGUgdHJlZSBvZiB0aGUgZ2FpbiByYXRpbyB1c2luZyAxMCBmb2xkcw0KDQpgYGB7ciAsIGZpZy5oZWlnaHQ9NzAsIGZpZy53aWR0aD05MH0NCnNldC5zZWVkKDEwKQ0KY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTAsIHJldHVyblJlc2FtcD0iYWxsIiwgc2F2ZVByZWRpY3Rpb25zPSJmaW5hbCIpDQpnYWluUmF0aW8xMCA8LSB0cmFpbihzYWxhcnlfaW5fdXNkIH4gLiwgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsIG1ldGhvZCA9ICJKNDgiLHRyQ29udHJvbCA9IGN0cmwpDQpwbG90KGdhaW5SYXRpbzEwJGZpbmFsTW9kZWwpDQpgYGANCg0KdGhlIGZpcnN0IHNwbGl0dGluZyBhdHRyaWJ1dGUgdGhhdCB3YXMgY2hvb3NlbiBpcyB0aGUgIkV4cGVpcmVuY2UgbGV2ZWwiIGF0dHJpYnV0ZSBtZWFuaW5nIHRoYXQgaXQgcHJvYmFibHkgaGFzIGEgaGlnaCBpbmZvcm1hdGlvbiBnYWluIGFuZCBsb3cgc3BsaXRJbmZvKEVudHJvcHkgb2YgZGlzdHJpYnV0aW9uIG9mIHR1cGxlcyBpbnRvIHBhcnRpdGlvbikNCg0KIyMjIyMjIENvbmZ1c2lvbiBtYXRyaXggb2YgMTAgZm9sZHMgdXNpbmcgR2FpbiByYXRpbw0KDQpUaGUgZm9sbG93aW5nIGNvbmZ1c2lvbiBNYXRyaXggd2lsbCBzaG93IHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgY2xhc3NpZmllciB1c2luZyB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscyBhbmQgdGhlIGFjdHVhbCBjbGFzcyBsYWJlbHMgb2Ygb3VyIGRhdGFzZXQNCg0KYGBge3J9DQpnYWluUmF0aW8xMGNtID0gY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChnYWluUmF0aW8xMCRwcmVkJG9icywgZ2FpblJhdGlvMTAkcHJlZCRwcmVkKQ0KDQpnYWluUmF0aW8xMGNtDQoNCg0KYGBgDQoNCmhlcmUgdGhlIGNsYXNzaWZpZXIgc2hvd3MgYmV0dGVyIHBlcmZvcm1hbmNlIHdoZW4gdHJlYXRpbmcgInZlcnkgaGlnaCIgYW5kICJ2ZXJ5IGxvdyIgYXR0cmlidXRlcyBhcyBwb3NpdGl2ZSBjbGFzcy4gc2luY2UgdGhlICJ2ZXJ5IGhpZ2giIGNsYXNzIGlzIGJldHRlciBpbiBTZW5zaXRpdml0eSBhbmQgInZlcnkgbG93IiBpcyBiZXR0ZXIgaW4gU3BlY2lmaWNpdHkgYW5kIHByZWNpc2lvbiAoUG9zIFByZWQgVmFsdWUpDQoNCiMjIyMjIDUgRm9sZHMNCg0KVGhlIHRyZWUgb2YgdGhlIGdhaW4gcmF0aW8gdXNpbmcgNSBmb2xkcw0KDQpgYGB7ciAsIGZpZy5oZWlnaHQ9NzAsIGZpZy53aWR0aD05MH0NCnNldC5zZWVkKDEwKQ0KY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gNSwgcmV0dXJuUmVzYW1wPSJhbGwiLCBzYXZlUHJlZGljdGlvbnM9ImZpbmFsIikNCmdhaW5SYXRpbzUgPC0gdHJhaW4oc2FsYXJ5X2luX3VzZCB+IC4sIGRhdGEgPSBiYWxhbmNlZF9kYXRhc2V0LCBtZXRob2QgPSAiSjQ4Iix0ckNvbnRyb2wgPSBjdHJsKQ0KcGxvdChnYWluUmF0aW81JGZpbmFsTW9kZWwpDQpgYGANCg0KdGhlIHRyZWUgaXMgc2ltaWxhciB0byB0aGUgdHJlZSB0aGF0IHdhcyByZXN1bHRlZCBmcm9tIDEwIGZvbGRzLiBpdCBoYXMgY2hvb3NlICJFeHBlcmllbmNlIGxldmVsIiBhcyB0aGUgZmlyc3Qgc3BsaXR0aW5nIGF0dHJpYnV0ZSBhbmQgYW5kIHNlZW0gdG8gc2hvdyBzaW1pbGFyIGJlaGF2aW9yLg0KDQojIyMjIyMgQ29uZnVzaW9uIG1hdHJpeCBvZiA1IGZvbGRzIHVzaW5nIEdhaW4gcmF0aW8NCg0KVGhlIGZvbGxvd2luZyBjb25mdXNpb24gTWF0cml4IHdpbGwgc2hvdyB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGNsYXNzaWZpZXIgdXNpbmcgdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMgYW5kIHRoZSBhY3R1YWwgY2xhc3MgbGFiZWxzIG9mIG91ciBkYXRhc2V0DQoNCmBgYHtyfQ0KDQpnYWluUmF0aW81Y209Y2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChnYWluUmF0aW81JHByZWQkb2JzLCBnYWluUmF0aW81JHByZWQkcHJlZCkNCg0KZ2FpblJhdGlvNWNtDQoNCmBgYA0KDQp1bmxpa2UgdGhlIDEwIGZvbGRzLCBoZXJlIHRoZSBjbGFzc2lmaWVyIGhhcyB0aGUgYmVzdCBvdmVyYWxsIHBlcmZvcm1hbmNlIHdoZW4gY29uc2lkZXJpbmcgdGhlICJ2ZXJ5IGhpZ2giIGFzIHRoZSBwb3NpdGl2ZSBjbGFzcy4NCg0KIyMjIyMgMyBGb2xkcw0KDQpUaGUgdHJlZSBvZiB0aGUgZ2FpbiByYXRpbyB1c2luZyAzIGZvbGRzDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTcwLCBmaWcud2lkdGg9OTB9DQpzZXQuc2VlZCgxMCkNCmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDMsIHJldHVyblJlc2FtcD0iYWxsIiwgc2F2ZVByZWRpY3Rpb25zPSJmaW5hbCIpDQpnYWluUmF0aW8zIDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gIko0OCIsdHJDb250cm9sID0gY3RybCkNCnBsb3QoZ2FpblJhdGlvMyRmaW5hbE1vZGVsKQ0KYGBgDQoNCnRoZSB0cmVlIHNob3dzIHNpbWlsYXIgYmVoYXZpb3IgYXMgdGhlIHByZXZpb3VzIDIgdHJlZXMgdGhhdCByZXN1bHRlZCBmcm9tIHVzaW5nIDEwIGFuZCA1IGZvbGRzLg0KDQojIyMjIyMgQ29uZnVzaW9uIG1hdHJpeCBvZiAzIGZvbGRzIHVzaW5nIEdhaW4gcmF0aW8NCg0KVGhlIGZvbGxvd2luZyBjb25mdXNpb24gTWF0cml4IHdpbGwgc2hvdyB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGNsYXNzaWZpZXIgdXNpbmcgdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMgYW5kIHRoZSBhY3R1YWwgY2xhc3MgbGFiZWxzIG9mIG91ciBkYXRhc2V0DQoNCmBgYHtyfQ0KZ2FpblJhdGlvM2NtPWNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZ2FpblJhdGlvMyRwcmVkJG9icywgZ2FpblJhdGlvMyRwcmVkJHByZWQpDQoNCmdhaW5SYXRpbzNjbQ0KDQpgYGANCg0Kc2ltaWxhciB0byB0ZWggNSBmb2xkcywgdGhlICJ2ZXJ5IGhpZ2giIGNsYXNzIGhhcyB0aGUgYmVzdCBtZXRyaWNzLg0KDQojIyMjIyMgQW5hbHlzaXMgb2YgdGhlIGdhaW4gcmF0aW8gY2xhc3NpZmljYXRpb24NCg0KVGhlIG9ic2VydmVkIHN0cnVjdHVyZSBvZiB0aGUgdGhyZWUgZGVjaXNpb24gdHJlZXMgc2VlbXMgdG8gYmUgdGhlIHNhbWUgYW5kIGl0IGNhbiBiZSBzdW1tYXJpemVkIGFzIGZvbGxvd3M6DQoNCjEuICBSb290IE5vZGUgLSBFeHBlcmllbmNlIExldmVsOiBUaGUgaW5pdGlhbCBhdHRyaWJ1dGUgdXNlZCBmb3Igc3BsaXR0aW5nIHRoZSBkYXRhc2V0IGF0IHRoZSByb290IG5vZGUgaXMgdGhlICJleHBlcmllbmNlIGxldmVsLiIgVGhpcyBkaXZpZGVzIHRoZSB0cmVlIGludG8gdHdvIG1haW4gYnJhbmNoZXMgb3Igc3VidHJlZXM6DQogICAgLSAgIFJpZ2h0IFN1YnRyZWU6IFRoaXMgY29tcHJpc2VzIGluc3RhbmNlcyB3aXRoIFNlbmlvciAoU0UpIGFuZCBFeGVjdXRpdmUgKEVYKSBleHBlcmllbmNlIGxldmVscy4NCiAgICAtICAgTGVmdCBTdWJ0cmVlOiBUaGlzIGluY2x1ZGVzIGluZGl2aWR1YWxzIHdpdGggRW50cnkgKEVOKSBhbmQgTWlkIChNSSkgZXhwZXJpZW5jZSBsZXZlbHMuDQoyLiAgV2l0aGluIHRoZSByaWdodCBzdWJ0cmVlOg0KICAgIC0gICBJZiB0aGUgJ2V4cGVyaWVuY2UgbGV2ZWwnIGlzIDQgKEVYLCBFeGVjdXRpdmUgbGV2ZWwpICwgdGhlIHRyZWUgc3BsaXRzIGJhc2VkIG9uIHRoZSAnRW1wbG95ZWVfcmVzaWRlbmNlJyBhdHRyaWJ1dGUuIEl0IGNoZWNrcyB3aGV0aGVyIHRoZSAnRW1wbG95ZWVfcmVzaWRlbmNlJyBpcyAnTGF0aW4gQW1lcmljYS4nDQogICAgLSAgIElmICdFbXBsb3llZV9yZXNpZGVuY2UnIGRvZXMgbm90IGVxdWFsICdMYXRpbiBBbWVyaWNhLCcgdGhlIGRpZmZlcmVudGlhdGlvbiBjb250aW51ZXMgd2l0aCB0aGUgJ3JlbW90ZV9yYXRpbycgYXR0cmlidXRlLCBmdXJ0aGVyIGRpdmlkaW5nIHRoZSB0cmVlLg0KMy4gIFdpdGhpbiB0aGUgbGVmdCBzdWJ0cmVlOg0KICAgIC0gICBJZiB0aGUgJ2V4cGVyaWVuY2UgbGV2ZWwnIGlzIDEgKEVOLCBFbnRyeSBsZXZlbCksIHRoZSB0cmVlIGRpdmlkZSBiYXNlZCBvbiB0aGUgJ0VtcGxveWVlX3Jlc2lkZW5jZScgYXR0cmlidXRlLCBzcGVjaWZpY2FsbHkgY2hlY2tpbmcgZm9yICdTdWItU2FoYXJhbiBBZnJpY2EuJw0KICAgIC0gICBJZiB0aGUgJ2V4cGVyaWVuY2UgbGV2ZWwnIGlzIDIgKE1JLCBNaWQgbGV2ZWwpLCBpdCBhbHNvIGJyYW5jaGVzIGJhc2VkIG9uICdFbXBsb3llZV9yZXNpZGVuY2UsJyBidXQgaW4gdGhpcyBjYXNlLCBsb29raW5nIHRvIHNlZSBpZiBpdCBlcXVhbHMgJ05vcnRoIEFtZXJpY2EuJw0KDQpUaGUgZGVjaXNpb24gdHJlZSBjb250aW51ZXMgdG8gc2VsZWN0IHRoZSBtb3N0IGFwcHJvcHJpYXRlIGF0dHJpYnV0ZXMgZm9yIHNwbGl0dGluZyBhdCBlYWNoIG5vZGUsIHByb2dyZXNzaXZlbHkgcmVmaW5pbmcgdGhlIGRlY2lzaW9uIHByb2Nlc3MgdW50aWwgaXQgcmVhY2hlcyB0aGUgbGVhdmVzLCB3aGVyZSBmaW5hbCBjbGFzcyBsYWJlbHMgYXJlIGFzc2lnbmVkIHRvIHRoZSBpbnN0YW5jZXMuDQoNCiMjIyMjIyBTZW5zaXRpdml0eSwgQWNjdXJhY3ksIFNwZWNpZml0eSBhbmQgcHJlY2lzaW9uIG9mIGFsbCAzLDUgYW5kIDEwIGZvbGRzIHVzaW5nIEdhaW4gcmF0aW8NCg0KYGBge3J9DQpyYmluZCgiMTAgRm9sZHMiPW1hY3JvKGdhaW5SYXRpbzEwY20pLCAiNSBGb2xkcyI9bWFjcm8oZ2FpblJhdGlvNWNtKSwgIjMgRm9sZHMiPW1hY3JvKGdhaW5SYXRpbzNjbSkgICkgDQpgYGANCg0KQmFzZWQgb24gdGhlIGV2YWx1YXRpb24gbWV0cmljcyBvZiBhdmVyYWdlIFNlbnNpdGl2aXR5LFByZWNpc2lvbiAsU3BlY2lmaWNpdHksIGFuZCBBY2N1cmFjeSwgaXQgaXMgZXZpZGVudCB0aGF0IHRoZSBnYWluIHJhdGlvIG1vZGVsLCBidWlsdCB1c2luZyBhIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBhcHByb2FjaCwgZXhoaWJpdHMgc3VwZXJpb3IgcGVyZm9ybWFuY2UgY29tcGFyZWQgdG8gdGhlIG90aGVyIHR3byBtb2RlbHMuIEhvd2V2ZXIsIGl0J3Mgd29ydGggbm90aW5nIHRoYXQgdGhlIGRpZmZlcmVuY2UgaW4gcGVyZm9ybWFuY2UgYmV0d2VlbiB0aGUgbW9kZWxzIGlzIHJlbGF0aXZlbHkgc21hbGwuDQoNCkEgZGV0YWlsZWQgZXhhbWluYXRpb24gb2YgdGhlIHJlc3VsdHMgZnJvbSB0aGUgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHJldmVhbHMgdGhhdCB0aGUgbW9kZWwgaGFzIGEgbm90YWJseSBoaWdoIHNwZWNpZmljaXR5IGNvbXBhcmVkIHRvIG90aGVyIG1ldHJpY3MuIFRoaXMgaGlnaCBzcGVjaWZpY2l0eSBzdWdnZXN0cyB0aGF0IHRoZSBtb2RlbCBpcyBwYXJ0aWN1bGFybHkgZWZmZWN0aXZlIGF0IGNvcnJlY3RseSBpZGVudGlmeWluZyBpbnN0YW5jZXMgdGhhdCBkbyBub3QgcGVydGFpbiB0byB0aGUgdGFyZ2V0IGNsYXNzLS0tZXNzZW50aWFsbHksIGl0IGFjY3VyYXRlbHkgcmVjb2duaXplcyB3aGVuIGV4YW1wbGVzIGFyZSBub3QgbWVtYmVycyBvZiB0aGUgc3BlY2lmaWVkIGNsYXNzLiBGb3IgZXhhbXBsZSwgaWYgdGhlIHBvc2l0aXZlIGNsYXNzIGluIHF1ZXN0aW9uIGlzICJIaWdoIiB0aGVuIHRoZSBtb2RlbCBpcyBhYmxlIHRvIGNvcnJlY3RseSBjbGFzc2lmeSB0dXBsZXMgdGhhdCBiZWxvbmcgdG8gIlZlcnkgTG93IiwgIk1lZGl1bSIsIGFuZCAiVmVyeSBIaWdoIi4NCg0KSG93ZXZlciwgcG9zc2Vzc2luZyBoaWdoIHNwZWNpZmljaXR5IGFsb25lIGRvZXMgbm90IGd1YXJhbnRlZSB0aGUgb3ZlcmFsbCBlZmZlY3RpdmVuZXNzIG9mIHRoZSBtb2RlbCwgYXMgYSB3ZWxsLXJvdW5kZWQgbW9kZWwgYWxzbyByZXF1aXJlcyBiYWxhbmNlZCBwZXJmb3JtYW5jZSBhY3Jvc3Mgb3RoZXIgbWV0cmljcy4gSW4gdGhpcyBjYXNlLCBpdHMgYWJpbGl0eSB0byBjYXB0dXJlIGFuZCBjbGFzc2lmeSBpbnN0YW5jZXMgdGhhdCBkbyBiZWxvbmcgdG8gdGhlIHBvc2l0aXZlIGNsYXNzIChhcyBtZWFzdXJlZCBieSBzZW5zaXRpdml0eSkgaXMgbm90IGFzIHJvYnVzdC4gRm9yIGEgbW9kZWwgdG8gYmUgY29uc2lkZXJlZCB0cnVseSBlZmZlY3RpdmUsIGl0IHdvdWxkIG5lZWQgdG8gZGVtb25zdHJhdGUgc3Ryb25nIHBlcmZvcm1hbmNlIGluIGFsbCBtZXRyaWNzIHNwZWNpZmljaXR5IGFuZCBzZW5zaXRpdml0eSwgZW5zdXJpbmcgaXQgY2FuIGFjY3VyYXRlbHkgZGlzdGluZ3Vpc2ggYm90aCBuZWdhdGl2ZSBhbmQgcG9zaXRpdmUgaW5zdGFuY2VzIGFzIHdlbGwgYXMgYWNjdXJhY3kgcHJlY2lzaW9uLg0KDQojIyMjIEluZm9ybWF0aW9uIGdhaW4NCg0KSW5mb3JtYXRpb24gR2FpbiBpcyBhIG1ldHJpYyB1c2VkIHRvIGRlY2lkZSB3aGljaCBhdHRyaWJ1dGUgdG8gY2hvb3NlIGZvciBzcGxpdHRpbmcgdGhlIGRhdGEgYXQgZWFjaCBub2RlIGluIHRoZSBkZWNpc2lvbiB0cmVlLiBGb3IgYSBnaXZlbiBkYXRhc2V0LCB0aGUgSW5mb3JtYXRpb24gR2FpbiBvZiBhbiBhdHRyaWJ1dGUgaXMgY2FsY3VsYXRlZCBieSBjb21wYXJpbmcgdGhlIGVudHJvcHkgYmVmb3JlIGFuZCBhZnRlciB0aGUgZGF0YXNldCBpcyBzcGxpdCBiYXNlZCBvbiB0aGF0IGF0dHJpYnV0ZS4gVGhlIGF0dHJpYnV0ZSB3aXRoIHRoZSBoaWdoZXN0IEluZm9ybWF0aW9uIEdhaW4gaXMgY2hvc2VuIGFzIHRoZSBzcGxpdHRpbmcgYXR0cmlidXRlLg0KDQojIyMjIyAxMCBGb2xkcw0KDQpUaGUgdHJlZSBvZiB0aGUgaW5mb3JtYXRpb24gZ2FpbiB1c2luZyAxMCBmb2xkcw0KDQpgYGB7ciwgZmlnLmhlaWdodD03MCwgZmlnLndpZHRoPTkwfQ0Kc2V0LnNlZWQoMTApDQpjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCwgcmV0dXJuUmVzYW1wPSJhbGwiLCBzYXZlUHJlZGljdGlvbnM9ImZpbmFsIikNCg0KDQppbmZvR2FpbjEwIDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gIkM1LjAiLHRyQ29udHJvbCA9IGN0cmwpDQoNCmM1bW9kZWwgPC0gQzUuMChzYWxhcnlfaW5fdXNkIH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgICAgIHRyaWFscyA9IGluZm9HYWluMTAkYmVzdFR1bmUkdHJpYWxzLCANCiAgICAgICAgICAgICAgICAgICAgICAgcnVsZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IEM1LjBDb250cm9sKHdpbm5vdyA9IGluZm9HYWluMTAkYmVzdFR1bmUkd2lubm93KSkNCnBsb3QoYzVtb2RlbCkNCmBgYA0KDQpmcm9tIHRoZSB0cmVlLCB0aGUgImV4cGVyaW5jZSBsZXZlbCIgYXR0cmlidXRlIHdhcyB0aGUgZmlyc3Qgc2VsZWN0ZWQgc3BsaXR0aW5nIGF0dHJpYnV0ZSBtZWFuaW5nIHRoYXQgaXQgaGFzIHRoZSBoaWdoZXN0IGluZm9ybWF0aW9uIGdhaW4gYW1vbmcgYWxsIGF0dHJpYnV0ZXMNCg0KIyMjIyMjIENvbmZ1c2lvbiBtYXRyaXggb2YgMTAgZm9sZHMgdXNpbmcgSW5mb3JtYXRpb24gZ2Fpbg0KDQpUaGUgZm9sbG93aW5nIGNvbmZ1c2lvbiBNYXRyaXggd2lsbCBzaG93IHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgY2xhc3NpZmllciB1c2luZyB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscyBhbmQgdGhlIGFjdHVhbCBjbGFzcyBsYWJlbHMgb2Ygb3VyIGRhdGFzZXQNCg0KYGBge3J9DQppbmZvR2FpbjEwY209IGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoaW5mb0dhaW4xMCRwcmVkJG9icywgaW5mb0dhaW4xMCRwcmVkJHByZWQpDQoNCmluZm9HYWluMTBjbQ0KDQpgYGANCg0Kc2ltaWxhciB0byB0aGUgdHJlZXMgZnJvbSB0aGUgZ2luaSBpbmRleCBhbmQgZ2FpbiByYXRpbywgdGhlIGNsYXNzaWZpZXIgc2VlbSB0byBoYXZlIGJldHRlciBwZXJmb3JtYW5jZSB3aGVuIHRyZWF0aW5nIHRoZSAidmVyeSBoaWdoIiBjbGFzcyBhcyB0aGUgcG9zaXRpdmUgY2xhc3MNCg0KIyMjIyMgNSBGb2xkcw0KDQpUaGUgdHJlZSBvZiB0aGUgaW5mb3JtYXRpb24gZ2FpbiB1c2luZyA1IGZvbGRzDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTcwLCBmaWcud2lkdGg9OTB9DQpzZXQuc2VlZCgxMCkNCmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDUsIHJldHVyblJlc2FtcD0iYWxsIiwgc2F2ZVByZWRpY3Rpb25zPSJmaW5hbCIpDQoNCg0KaW5mb0dhaW41IDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gIkM1LjAiLHRyQ29udHJvbCA9IGN0cmwpDQoNCmM1bW9kZWwgPC0gQzUuMChzYWxhcnlfaW5fdXNkIH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgICAgIHRyaWFscyA9IGluZm9HYWluNSRiZXN0VHVuZSR0cmlhbHMsIA0KICAgICAgICAgICAgICAgICAgICAgICBydWxlcyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gQzUuMENvbnRyb2wod2lubm93ID0gaW5mb0dhaW41JGJlc3RUdW5lJHdpbm5vdykpDQpwbG90KGM1bW9kZWwpDQpgYGANCg0KdGhlIHRyZWUgaGFzIHNpbWlsYXIgYmVoYXZpb3IgYXMgdGhlIDEwIGZvbGRzIGluZm9ybWF0aW9uIGdhaW4gdHJlZQ0KDQojIyMjIyMgQ29uZnVzaW9uIG1hdHJpeCBvZiA1IGZvbGRzIHVzaW5nIEluZm9ybWF0aW9uIGdhaW4NCg0KVGhlIGZvbGxvd2luZyBjb25mdXNpb24gTWF0cml4IHdpbGwgc2hvdyB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGNsYXNzaWZpZXIgdXNpbmcgdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMgYW5kIHRoZSBhY3R1YWwgY2xhc3MgbGFiZWxzIG9mIG91ciBkYXRhc2V0DQoNCmBgYHtyfQ0KaW5mb0dhaW41Y20gPSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGluZm9HYWluNSRwcmVkJG9icywgaW5mb0dhaW41JHByZWQkcHJlZCkNCg0KaW5mb0dhaW41Y20NCg0KYGBgDQoNCnRoZSBjbGFzc2lmaWVyIHNob3dzIHZlcnkgY2xvc2UgcGVyZm9ybWFuY2UgdG8gdGhlIDEwIGZvbGRzIGluZm9ybWF0aW9uIGdhaW4gbW9kZWwNCg0KIyMjIyMgMyBGb2xkcw0KDQpUaGUgdHJlZSBvZiB0aGUgaW5mb3JtYXRpb24gZ2FpbiB1c2luZyAzIGZvbGRzDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTcwLCBmaWcud2lkdGg9OTB9DQpzZXQuc2VlZCgxMCkNCmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDMsIHJldHVyblJlc2FtcD0iYWxsIiwgc2F2ZVByZWRpY3Rpb25zPSJmaW5hbCIpDQoNCg0KaW5mb0dhaW4zIDwtIHRyYWluKHNhbGFyeV9pbl91c2QgfiAuLCBkYXRhID0gYmFsYW5jZWRfZGF0YXNldCwgbWV0aG9kID0gIkM1LjAiLHRyQ29udHJvbCA9IGN0cmwpDQoNCmM1bW9kZWwgPC0gQzUuMChzYWxhcnlfaW5fdXNkIH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJhbGFuY2VkX2RhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgICAgIHRyaWFscyA9IGluZm9HYWluMyRiZXN0VHVuZSR0cmlhbHMsIA0KICAgICAgICAgICAgICAgICAgICAgICBydWxlcyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gQzUuMENvbnRyb2wod2lubm93ID0gaW5mb0dhaW4zJGJlc3RUdW5lJHdpbm5vdykpDQpwbG90KGM1bW9kZWwpDQpgYGANCg0KdGhlIHZpc2lzYmxlIHBhcnRzIG9mIHRoZSB0cmVlIHNlZW0gdG8gYmVoYXZlIHRoZSBzYW1lIGFzIHRoZSBwcmV2b2l1cyAyIGZvbGQgc2l6ZXMtIDEwIGFuZCA1Lg0KDQojIyMjIyMgQ29uZnVzaW9uIG1hdHJpeCBvZiAzIGZvbGRzIHVzaW5nIEluZm9ybWF0aW9uIGdhaW4NCg0KVGhlIGZvbGxvd2luZyBjb25mdXNpb24gTWF0cml4IHdpbGwgc2hvdyB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGNsYXNzaWZpZXIgdXNpbmcgdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMgYW5kIHRoZSBhY3R1YWwgY2xhc3MgbGFiZWxzIG9mIG91ciBkYXRhc2V0DQoNCmBgYHtyfQ0KaW5mb0dhaW4zY20gPSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGluZm9HYWluMyRwcmVkJG9icywgaW5mb0dhaW4zJHByZWQkcHJlZCkNCg0KaW5mb0dhaW4zY20NCg0KYGBgDQoNCnNpbmNlIHRoZSB0cmVlIGlzIGVzc2VudGlhbGx5IHNpbWlsYXIgdG8gdGhlIHByZXZpb3VzIHR3byBpbmZvcm1hdGlvbiBnYWluIHRyZWVzIHRoZSByZXN1bHRzIHRoYXQgdGhpcyB0cmVlIHNob3dzIGlzIHZlcnkgY2xvc2UgaW4gcGVyZm9ybWFuY2UgdG8gdGhlbSBhcyB3ZWxsLg0KDQojIyMjIyMgQW5hbHlzaXMgb2YgdGhlIGluZm9ybWF0aW9uIGdhaW4gY2xhc3NpZmljYXRpb24NCg0KVGhlIG9ic2VydmVkIHN0cnVjdHVyZSBvZiB0aGUgdGhyZWUgZGVjaXNpb24gdHJlZXMgc2VlbXMgdG8gYmUgdGhlIHNhbWUgYW5kIGl0IGNhbiBiZSBzdW1tYXJpemVkIGFzIGZvbGxvd3M6DQoNCjEuICBSb290IE5vZGUgLSBFeHBlcmllbmNlIExldmVsOiBUaGUgaW5pdGlhbCBhdHRyaWJ1dGUgdXNlZCBmb3Igc3BsaXR0aW5nIHRoZSBkYXRhc2V0IGF0IHRoZSByb290IG5vZGUgaXMgdGhlICJleHBlcmllbmNlIGxldmVsLiIgVGhpcyBkaXZpZGVzIHRoZSB0cmVlIGludG8gdHdvIG1haW4gYnJhbmNoZXMgb3Igc3VidHJlZXM6DQoNCiAgICAtICAgUmlnaHQgU3VidHJlZTogVGhpcyBjb21wcmlzZXMgaW5zdGFuY2VzIHdpdGggU2VuaW9yIChTRSkgYW5kIEV4ZWN1dGl2ZSAoRVgpIGV4cGVyaWVuY2UgbGV2ZWxzLg0KICAgIC0gICBMZWZ0IFN1YnRyZWU6IFRoaXMgaW5jbHVkZXMgaW5kaXZpZHVhbHMgd2l0aCBFbnRyeSAoRU4pIGFuZCBNaWQgKE1JKSBleHBlcmllbmNlIGxldmVscy4NCg0KMi4gIFdpdGhpbiB0aGUgcmlnaHQgc3VidHJlZTogSW4gdGhlIHJpZ2h0IHN1YiB0cmVlIGlmIHRoZSBleHBlcmllbmNlIGxldmVsIGlzIDQoRVgpIHRoZSB0cmVlIHdpbGwgYmUgZGl2aWRlZCBiYXNlZCBvbiAiQ29tcGFueSBsb2NhdGlvbiINCg0KMy4gIFdpdGhpbiB0aGUgbGVmdCBzdWJ0cmVlOiBJbiB0aGUgbGVmdCBzdWJ0cmVlIGl0IHdpbGwgZGl2aWRlIHRoZSB0cmVlIGZvciBib3RoIHR3byBleHBlcmllbmNlIGxldmVscyAxKEVOKSBhbmQgMihNSSkgYmFzZWQgb24gImVtcGxveWVlIHJlc2lkZW5jZSIgYW5kIHdoZW4gdGhlICJlbXBsb3llZSByZXNpZGVuY2UiIGlzICJOb3J0aCBBbWVyaWNhIiB0aGUgdHJlZSB3aWxsIGJlIGZ1cnRoZXIgZGl2aWRlZCBiYXNlZCBvbiAic2FsYXJ5IGN1cnJlbmN5IiBhbmQgd2hlbiB0aGlzIGF0dHJpYnV0ZSBpcyBlcXVhbCB0byAiVVNEIiB0aGUgZGl2aXNpb24gd2lsbCBiZSBiYXNlZCBvbiB0aGUgImpvYiB0aXRsZSIgYXR0cmlidXRlDQoNClRoZSBkZWNpc2lvbiB0cmVlIGNvbnRpbnVlcyB0byBzZWxlY3QgdGhlIG1vc3QgYXBwcm9wcmlhdGUgYXR0cmlidXRlcyBmb3Igc3BsaXR0aW5nIGF0IGVhY2ggbm9kZSwgcHJvZ3Jlc3NpdmVseSByZWZpbmluZyB0aGUgZGVjaXNpb24gcHJvY2VzcyB1bnRpbCBpdCByZWFjaGVzIHRoZSBsZWF2ZXMsIHdoZXJlIGZpbmFsIGNsYXNzIGxhYmVscyBhcmUgYXNzaWduZWQgdG8gdGhlIGluc3RhbmNlcy4NCg0KIyMjIyMjIFNlbnNpdGl2aXR5LCBBY2N1cmFjeSwgU3BlY2lmaXR5IGFuZCBwcmVjaXNpb24gb2YgYWxsIDMsNSBhbmQgMTAgZm9sZHMgdXNpbmcgSW5mb3JtYXRpb24gZ2Fpbg0KDQpgYGB7cn0NCnJiaW5kKCIxMCBGb2xkcyI9bWFjcm8oaW5mb0dhaW4xMGNtKSwgIjUgRm9sZHMiPW1hY3JvKGluZm9HYWluNWNtKSwgIjMgRm9sZHMiPW1hY3JvKGluZm9HYWluM2NtKSAgKSANCmBgYA0KDQpCYXNlZCBvbiB0aGUgcHJvdmlkZWQgc2Vuc2l0aXZpdHksIHNwZWNpZmljaXR5LCBwcmVjaXNpb24sIGFuZCBhY2N1cmFjeSB2YWx1ZXMgdGhlcmUgaXNuJ3QgYSBjbGVhciBpbmRpY2F0aW9uIG9mIHRoZSBzdXBlcmlvcml0eSBvZiBvbmUgZm9sZCBvdmVyIGFub3RoZXIgZm9yIEluZm9ybWF0aW9uIEdhaW4gbW9kZWwgLndlIG1heSBuZWVkIHRvIGNvbnNpZGVyIGFkZGl0aW9uYWwgZmFjdG9ycyBvciBjb25kdWN0IGZ1cnRoZXIgYW5hbHlzaXMgdG8gbWFrZSBhIHdlbGwtaW5mb3JtZWQgZGVjaXNpb24uIGFzIGNhbiBiZSBzZWVuIGluIHRoZSB0YWJsZSB0aGUgMTAgZm9sZHMgaGFzIHRoZSBiZXN0IFNwZWNpZmljaXR5IGFuZCBQcmVjaXNpb24sIG1lYW53aGlsZSB0aGUgNSBmb2xkcyBoYXMgdGhlIGJlc3QgU2Vuc2l0aXZpdHkgYW5kIEFjY3VyYWN5Lg0KDQojIyMgQ2x1c3RlcmluZw0KDQpEYXRhIGNsdXN0ZXJpbmcgaXMgYSBwcm9jZXNzIHRvIHBhcnRpdGlvbiBkYXRhIGludG8gZ3JvdXBzIG9yIGNsdXN0ZXJzLGl0IGlzIGFuIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBwcm9jZXNzLCB3aGljaCBpcyBleGN1dGVkIHdpdGhvdXQga25vd2luZyB0aGUgY2xhc3MgbGFiZWwgb2YgdGhlIHRyYWluaW5nIGRhdGEuIFRoZSBkYXRhIGluIHRoZSBzYW1lIGdyb3VwICJjbHVzdGVyIiBhcmUgc2ltaWxhciB0byBvbmUgYW5vdGhlciBhbmQgZGlmZmVyZW50IGZyb20gZGF0YSBpbiBvdGhlciBjbHVzdGVycy4gQW5kIGZvciB0aGlzIGRhdGEgbWluaW5nIHRhc2sgV2Ugd2lsbCB1dGlsaXplIGstbWVhbnMgY2x1c3RlcmluZy4NCg0KIyMjIyAxLSBwcmVwcmVvY2Vzc2luZw0KDQp3ZSB3aWxsIGVuY29kZSB0aGUgcmVzdCBvZiBmYWN0b3IgY29sdW1ucyB0byB0cmFuc2Zvcm0gdGhlbSBpbnRvIG51bWVyaWMgdHlwZXMgYmVmb3JlIGNsdXN0ZXJpbmcsIGVuYWJsaW5nIG1lYW5pbmdmdWwgZGlzdGFuY2UgY2FsY3VsYXRpb25zIHVzaW5nIGttZWFucyBhbmQgb3RoZXIgZm9ybXVsYXMsIGFuZCBhbGxvd2luZyBmb3IgbWF4aW11bSBmbGV4aWJpbGl0eSBpbiBkYXRhIHByb2Nlc3NpbmcgYW5kIGludGVycHJldGF0aW9uLiB3ZSB3aWxsIGFsc28gcmVtb3ZlIHRoZSBjbGFzcyBsYWJlbCBmcm9tIHRoZSBkYXRhc2V0IGFzIGNsdXN0ZXJpbmcgaXMgYW4gdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHByb2Nlc3MsIGFuZCB3ZSB3aWxsIHByZXNlcnZlIHRoaXMgY2xhc3MgbGFiZWwgaW4gYW4gYXR0cmlidXRlIGZvciBsYXRlciB1c2UuIGxhc3RseSwgd2Ugd2lsbCBzY2FsZSBhbGwgbnVtZXJpYyBhdHRyaWJ1dGVzIGluIHRoZSBkYXRhc2V0IHNvIHRoZXkgd2lsbCBiZSBzdGFuZGFyaXplZC4NCg0KYGBge3J9DQoNCiMgdmlldyBkYXRhDQoNCmRhdGFzZXQzIDwtIGRhdGFzZXQyDQpWaWV3KGRhdGFzZXQzKQ0KDQojIFJlc2VydmUgdGhlIHNhbGFyeV9pbl91c2QgKHRoZSBjbGFzcyBsYWJlbCkgY29sdW1uIGluIGFuIGF0dHJpYnV0ZSBiZWZvcmUgcmVtb3ZpbmcgaXQgZnJvbSB0aGUgZGF0YXNldCBmb3IgY2x1c3RlcmluZw0KDQpjbGFzc0xhYmVsIDwtIGRhdGFzZXQzWywgNV0gDQoNCg0KIyBSZW1vdmUgdGhlIGNsYXNzIGxhYmxlIGZyb20gdGhlIGRhdGFzZXQNCg0KZGF0YXNldDMgPC0gZGF0YXNldDNbLCAtNV0NCg0KIyBlbmNvZGluZyBqb2JfdGl0bGUgdmFyaWFibGUNCg0KZGF0YXNldDMkam9iX3RpdGxlID0gZmFjdG9yKGRhdGFzZXQzJGpvYl90aXRsZSwgbGV2ZWxzPWMoIkFuYWx5c3QiLCAiQXJjaGl0ZWN0IiwgIkVuZ2luZWVyIiwgIkxlYWRlcnNoaXAiLCAiQ29uc3VsdGFudC9TcGVjaWFsaXN0IiwiQ3liZXIgU2VjdXJpdHkiLCJPdGhlcnMiICksIGxhYmVscz1jKDQsMSwyLDUsMyw2LDcpKQ0KDQojIGVuY29kaW5nIHNhbGFyeV9jdXJyZW5jeSB2YXJpYWJsZQ0KDQpkYXRhc2V0MyRzYWxhcnlfY3VycmVuY3kgPSBmYWN0b3IoZGF0YXNldDMkc2FsYXJ5X2N1cnJlbmN5LCBsZXZlbHM9YygiVVNEIiwiQlJMIiwiR0JQIiwiRVVSIiwiSU5SIiwiQ0FEIiwiQ0hGIiwiREtLIiwiU0dEIiwiQVVEIiwiU0VLIiwiTVhOIiwiSUxTIiwiUExOIiwiTk9LIiwiSURSIiwiTlpEIiwiSFVGIiwiWkFSIiwiVFdEIiwiUlVCIiksIGxhYmVscz1jKDEsMiwzLDQsNSw2LDcsOCw5LDEwLDExLDEyLDEzLDE0LDE1LDE2LDE3LDE4LDE5LDIwLDIxKSkNCg0KIyBlbmNvZGluZyBlbXBsb3llZV9yZXNpZGVuY2UgdmFyaWFibGUNCg0KZGF0YXNldDMkZW1wbG95ZWVfcmVzaWRlbmNlID0gZmFjdG9yKGRhdGFzZXQzJGVtcGxveWVlX3Jlc2lkZW5jZSwgbGV2ZWxzPWMoIk5vcnRoIEFtZXJpY2EiLCJMYXRpbiBBbWVyaWNhICYgQ2FyaWJiZWFuIiwiU3ViLVNhaGFyYW4gQWZyaWNhIiwgIkV1cm9wZSAmIENlbnRyYWwgQXNpYSIsIkVhc3QgQXNpYSAmIFBhY2lmaWMiLCJTb3V0aCBBc2lhIiwiTWlkZGxlIEVhc3QgJiBOb3J0aCBBZnJpY2EiKSwgbGFiZWxzPWMoMSwyLDMsNCw1LDYsNykpDQoNCiMgZW5jb2RpbmcgY29tcGFueV9sb2NhdGlvbiB2YXJpYWJsZQ0KDQpkYXRhc2V0MyRjb21wYW55X2xvY2F0aW9uID0gZmFjdG9yKGRhdGFzZXQzJGNvbXBhbnlfbG9jYXRpb24sIGxldmVscz1jKCJOb3J0aCBBbWVyaWNhIiwiTGF0aW4gQW1lcmljYSAmIENhcmliYmVhbiIsIlN1Yi1TYWhhcmFuIEFmcmljYSIsICJFdXJvcGUgJiBDZW50cmFsIEFzaWEiLCJFYXN0IEFzaWEgJiBQYWNpZmljIiwiU291dGggQXNpYSIsIk1pZGRsZSBFYXN0ICYgTm9ydGggQWZyaWNhIiwgIkFRIiwgIlVNIiksIGxhYmVscz1jKDEsMiwzLDQsNSw2LDcsOCw5KSkNCg0KDQogDQojRGF0YSB0eXBlcyB0byBiZSB0cmFuc2Zvcm1lZCBpbnRvIG51bWVyaWMgdHlwZXMgYmVmb3JlIGNsdXN0ZXJpbmcNCiNUcmFuc2Zvcm1pbmcgYWxsIG5vbi1udW1lcmljIGF0dHJpYnV0ZXMgdG8gbnVtZXJpYyB0eXBlDQoNCg0KZGF0YXNldDMkZXhwZXJpZW5jZV9sZXZlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhc2V0MyRleHBlcmllbmNlX2xldmVsKSkNCg0KZGF0YXNldDMkam9iX3RpdGxlIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFzZXQzJGpvYl90aXRsZSkpDQoNCmRhdGFzZXQzJHNhbGFyeV9jdXJyZW5jeSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhc2V0MyRzYWxhcnlfY3VycmVuY3kpKQ0KDQpkYXRhc2V0MyRlbXBsb3llZV9yZXNpZGVuY2UgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YXNldDMkZW1wbG95ZWVfcmVzaWRlbmNlKSkNCg0KZGF0YXNldDMkY29tcGFueV9sb2NhdGlvbiA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhc2V0MyRjb21wYW55X2xvY2F0aW9uKSkNCg0KZGF0YXNldDMkY29tcGFueV9zaXplIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFzZXQzJGNvbXBhbnlfc2l6ZSkpDQoNCiMgdml3ZSB0aGUgY2xhc3Mgb2YgYXR0cmlidXRlcyB0byBlbnN1cmUgdGhleSBoYXZlIHRyYW5zZm9ybWVkIHRvIG51bWVyaWMNCnNhcHBseShkYXRhc2V0MywgY2xhc3MpDQoNCg0KI3NjYWxlIGFsbCBhdHRyaWJ1dGVzIGluIHRoZSBkYXRhc2V0IHNvIHRoZXkgd291bGQgYmUgc3RhbmRhcmRpemVkIA0KZGF0YXNldDMgPC0gc2NhbGUoZGF0YXNldDMpDQoNCmBgYA0KDQojIyMjIDItIEstbWVhbnMNCg0KQWZ0ZXIgcHJlcHJvY2Vzc2luZyB0aGUgZGF0YSwgbm93IHdlIGFyZSByZWFkeSB0byBwZXJmb3JtIHRoZSBjbHVzdGVyaW5nIHByb2Nlc3MsIHdlIHdpbGwgdXNlIHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcsIGl0IGlzIGEgY2x1c3RlcmluZyBtZXRob2QgdGhhdCBhaW1zIHRvIG1pbmltaXplIHRoZSBzdW0gb2Ygc3F1YXJlZCBkaXN0YW5jZXMgYmV0d2VlbiBlYWNoIGRhdGEgcG9pbnQgYW5kIHRoZSBjZW50cm9pZCBvZiBpdHMgYXNzaWduZWQgY2x1c3RlciBieSBpdGVyYXRpdmVseSB1cGRhdGluZyBjbHVzdGVyIGFzc2lnbm1lbnRzIGFuZCBjZW50cm9pZHMuDQoNCiMjIyMgMy0gQ2hvb3NpbmcgbnVtYmVyIG9mIGNsdXN0ZXJzIChrKQ0KDQpXZSB3aWxsIGNob29zZSAzIGRpZmZlcmVudCBudW1iZXJzIHRvIHBlcmZvcm0gdGhlIGstbWVhbnMgY2x1c3RlcmluZyBvbiwgb25lIG9mIHRoZSBudW1iZXJzIHNob3VsZCBiZSByZWxhdGV2aWx5IGxhcmdlLCB0aGUgc2Vjb25kIHNob3VsZCBiZSBpbiB0aGUgbWlkZGxlIGFuZCB0aGUgbGFzdCBzaG91bGQgYmUgc21hbGwuIFRoaXMgd2F5IHdlIHdpbGwgY292ZXIgdGhlIHBvc3NpYmxlIG91dGNvbWVzIGFuZCBjbHVzdGVyaW5nIHJlc3VsdHMuDQoNCiMjIyMjIGEtIFNpbGhvdWV0dGUgbWV0aG9kDQoNCk5vdyB3ZSB3aWxsIGFwcGx5IFNpbGhvdWV0dGUgbWV0aG9kIHRvIGZpbmQgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGssIHdlIHdpbGwgYWxzbyBwbG90IGEgZ3JhcGggd2hlcmUgeC1heGlzIHJlcHJlc2VudCB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGFuZCB5LWF4aXMgcmVwcmVzZW50IHRoZSBhdmVyYWdlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQNCg0KYGBge3J9DQoNCmZ2aXpfbmJjbHVzdChkYXRhc2V0Mywga21lYW5zLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpKw0KICBsYWJzKHN1YnRpdGxlID0gIlNpbGhvdWV0dGUgbWV0aG9kIikNCg0KYGBgDQoNCmFzIHNlZW4gYnkgdGhlIGdyYXBoLCB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGsgdGhhdCBtYXhpbWl6ZXMgdGhlIGF2ZXJhZ2UgU2lsaG91ZXR0ZSBjb2VmZmljaWVudCBpcyAyLCBzbyB3ZSB3aWxsIHVzZSBpdCBmb3IgY2x1c3RlcmluZy4NCg0KIyMjIyMgYi0gRWxib3cgbWV0aG9kDQoNClRoaXMgbWV0aG9kIGRldGVybWluZXMgdGhlIG51bWJlciBvZiBjbHVzdGVycyBhY2NvcmRpbmcgdG8gdGhlIHR1cm5pbmcgcG9pbnQgaW4gYSBjdXJ2ZSwgdGhlIGN1cnZlIGlzIHBsb3R0ZWQgdXNpbmcgdGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmUgKFdTUykgYXMgaW4geS1heGlzICwgYW5kIE5vLiBjbHVzdGVycyBpbiB4LWF4aXMNCg0KYGBge3J9DQoNCmZ2aXpfbmJjbHVzdChkYXRhc2V0Mywga21lYW5zLCBtZXRob2QgPSAid3NzIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBsaW5ldHlwZSA9IDIpKw0KICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpDQoNCmBgYA0KDQpBcyBzaG93biwgdGhlIG51bWJlciBvZiBjbHVzdGVycyBrIHRoYXQgcmVwcmVzZW50cyB0aGUgdHVybmluZyBwb2ludCBpbiB0aGUgY3VydmUgaXMgNCwgc28gd2Ugd2lsbCB1c2UgaXQgZm9yIGNsdXN0ZXJpbmcuDQoNCkxhc3RseSwgd2Ugd2lsbCB1c2Ugaz0zIHNpbmNlIGl0IGFjaGVpdmVzIHRoZSBzZWNvbmQgaGlnaGVzdCBhdmVyYWdlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQsIGFuZCBzaW5jZSBpdCdzIGluIHRoZSBtaWRkbGUgYmV0d2VlbiAyIGFuZCA0IGl0IHdpbGwgc3RyaWtlIGEgYmFsYW5jZSBiZXR3ZWVuIGhhdmluZyB0b28gZmV3IGNsdXN0ZXJzIChrPTIpLCBhbmQgaGF2aW5nIHNldmVyYWwgY2x1c3RlcnMgKGs9NCksIFRodXMsIHRoaXMgY2hvaWNlIHdpbGwgaGF2ZSBhbiBhY2NlcHRhYmxlIGFjdXVyYWN5Lg0KDQojIyMjIGstbWVhbnMgY2x1c3RlcmluZywgdmlzdWFsaXphdGlvbiBhbmQgZXZhbHVhdGlvbg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgcGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgYW5kIHZpc3VhbGl6ZSBpdHMgcmVzdWx0IHVzaW5nIHRocmVlIGRpZmZlcmVudCBrJ3MgdGhhdCBoYXZlIGJlZW4gY2hvc2VuIGJlZm9yZWhhbmQsIHRoZW4gd2Ugd2lsbCBjb21wdXRlIFdTUyBhbmQgQmN1YmVkIHByZWNlaXNpb24gYW5kIHJlY2FsbCBhbmQgYXZlcmFnZSBzaWxob3VldHRlIGZvciBlYWNoIGNsdXN0ZXIgYXMgbWV0aG9kcyBvZiBldmFsdWF0aW5nIGNsdXN0ZXJpbmcgcmVzdWx0cy4NCg0KIyMjIyMgaz0yDQoNCmBgYHtyfQ0KDQojVXNlIHNlZWQgdG8gZ3VhcmFudGVlIHJlcGxpY2FiaWxpdHkgb2YgcmFuZG9tIHByb2Nlc3Nlcw0Kc2V0LnNlZWQoODk1MykNCg0KIyBydW4gay1tZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgMiBjbHVzdGVycw0Ka21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YXNldDMsIDIpDQoNCiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHQNCmttZWFucy5yZXN1bHQNCg0KIyB2aXN1YWxpemUgY2x1c3RlcmluZw0KZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhc2V0MykNCg0KDQojYXZlcmFnZSBzaWxob3VldHRlIGZvciBlYWNoIGNsdXN0ZXJzDQphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLGRpc3QoZGF0YXNldDMpKSANCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKQ0KDQojV2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgd3NzIA0Kd3NzIDwtIGttZWFucy5yZXN1bHQkdG90LndpdGhpbnNzDQpwcmludCh3c3MpDQoNCiNCQ3ViZWQNCmttZWFuc19jbHVzdGVyIDwtIGMoa21lYW5zLnJlc3VsdCRjbHVzdGVyKQ0KDQpncm91bmRfdHJ1dGggPC0gYyhjbGFzc0xhYmVsKQ0KDQpkYXRhIDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGttZWFuc19jbHVzdGVyLCBsYWJlbCA9IGdyb3VuZF90cnV0aCkNCg0KDQogIGJjdWJlZCA8LSBmdW5jdGlvbihkYXRhKSB7DQogIG4gPC0gbnJvdyhkYXRhKQ0KICB0b3RhbF9wcmVjZXNpb24gPC0gMA0KICB0b3RhbF9yZWNhbGwgPC0gMA0KDQpmb3IgKGkgaW4gMTpuKSB7DQogIGNsdXN0ZXIgPC0gZGF0YSRjbHVzdGVyW2ldDQogIGxhYmVsIDwtIGRhdGEkbGFiZWxbaV0NCiAgICANCiMgTnVtYmVyIG9mIG9iamVjdHMgaW4gdGhlIHNhbWUgY2F0ZWdvcnkgYW5kIGNsdXN0ZXINCmludGVyc2VjdGlvbiA8LSBzdW0oZGF0YSRsYWJlbFtkYXRhJGNsdXN0ZXIgPT0gY2x1c3Rlcl0gPT0gbGFiZWwpDQogICAgDQojIE51bWJlciBvZiBvYmplY3RzIHRoYXQgYXJlIGluIHRoZSBzYW1lIGNsdXN0ZXINCnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpDQogICAgDQojIE51bWJlciBvZiBvYmplY3RzIHRoYXQgaGF2ZSB0aGUgc2FtZSBjYXRlZ29yeQ0KdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbCkNCiAgICANCg0KdG90YWxfcHJlY2VzaW9uIDwtIHRvdGFsX3ByZWNlc2lvbiArIGludGVyc2VjdGlvbiAvdG90YWxfc2FtZV9jbHVzdGVyDQp0b3RhbF9yZWNhbGwgPC0gdG90YWxfcmVjYWxsICsgaW50ZXJzZWN0aW9uIC8gdG90YWxfc2FtZV9jYXRlZ29yeQ0KICB9DQoNCiAgIyBjb21wdXRlIGF2ZyBwcmVjaXNpb24gYW5kIHJlY2FsbA0KICBwcmVjaXNpb24gPC0gdG90YWxfcHJlY2VzaW9uIC8gbg0KICByZWNhbGwgPC0gdG90YWxfcmVjYWxsIC8gbg0KDQogIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSkgfQ0KDQoNCiMgY29tcHV0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCm1ldHJpY3MgPC0gYmN1YmVkKGRhdGEpDQoNCg0KcHJlY2lzaW9uIDwtIG1ldHJpY3MkcHJlY2lzaW9uDQpyZWNhbGwgPC0gbWV0cmljcyRyZWNhbGwNCg0KIyBQcmludCByZXN1bHRzDQpjYXQoIkJDdWJlZCBQcmVjaXNpb24gaXM6IiwgcHJlY2lzaW9uLCAiXG4iKQ0KY2F0KCJCQ3ViZWQgUmVjYWxsIGlzOiIsIHJlY2FsbCwgIlxuIikNCmBgYA0KDQp3ZSBjYW4gY29uY2x1ZGUgZnJvbSB0aGUgZ3JhcGggYW5kIHRoZSByZXN1bHRzIHRoYXQgdGhlIGs9MiBpcyB0aGUgb3B0aW1hbCBrLCBzaW5jZSB0aGVyZSBpcyBubyBvdmVybGFwcGluZyBiZXR3ZWVuIHRoZSB0d28gY2x1c3RlcnMsIHRoZSBkYXRhIGluIGEgY2x1c3RlciBhcmUgY2xvc2UgInNpbWlsYXIiIHRvIGVhY2ggb3RoZXIgYW5kIGRpc3NpbWlsYXIgdG8gZGF0YSBpbiB0aGUgb3RoZXIgY2x1c3Rlci4gQWxzbywgdGhlIHJlY2FsbCBpcyByZWxhdGl2ZWx5IGhpZ2ggKDAuNzEpIGFuZCBpcyB0aGUgaGlnaGVzdCBhbW9uZyB0aGUgaydzIGNob3NlbiwgdGhlIFByZWNpc2lvbiBpcyBsb3cgKDAuMjgpIHdoaWNoIGNvdWxkIGJlIGR1byB0byBwcmVzZW5jZSBvZiBvdXRsaWVycyBvciBzZW5zaXRpdml0eSB0byBJbml0aWFsIENlbnRyb2lkLiBXZSBjYW4gYWxzbyBub3RlIHRoYXQgdGhlIFdTUyBpcyA3Mjg3LjY1NywgaW5kaWNhdGluZyBhIGdvb2QgY29tcGFjdG5lc3Mgb2YgY2x1c3RlcnMsIGFuZCB0aGF0IG9iamVjdHMgaW4gYSBjbHVzdGVyIGFyZSBzaW1pbGFyIHRvIG9uZSBhbm90aGVyIG5vdGluZyB0aGF0IHRoZSBoaWdoZXIgdGhlIGssIHRoZSBsb3dlciB0aGUgV1NTLiBMYXN0bHksIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggaXMgMC4zNCB3aGljaCBpcyBjb25zaWRlcmVkIGhpZ2ggcmVmbGVjdGluZyBoaWdoIGludHJhLWNsdXN0ZXIgc2ltaWxhcml0eS4NCg0KIyMjIyMgaz0zDQoNCmBgYHtyfQ0KDQojVXNlIHNlZWQgdG8gZ3VhcmFudGVlIHJlcGxpY2FiaWxpdHkgb2YgcmFuZG9tIHByb2Nlc3Nlcw0Kc2V0LnNlZWQoODk1MykNCg0KIyBydW4gay1tZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgMyBjbHVzdGVycw0Ka21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YXNldDMsIDMpDQoNCiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHQNCmttZWFucy5yZXN1bHQNCg0KIyB2aXN1YWxpemUgY2x1c3RlcmluZw0KZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhc2V0MykNCg0KI2F2ZXJhZ2Ugc2lsaG91ZXR0ZSBmb3IgZWFjaCBjbHVzdGVycw0KYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlcixkaXN0KGRhdGFzZXQzKSkgDQpmdml6X3NpbGhvdWV0dGUoYXZnX3NpbCkNCg0KI1dpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIHdzcyANCndzcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcw0KcHJpbnQod3NzKQ0KDQojQkN1YmVkIA0Ka21lYW5zX2NsdXN0ZXIgPC0gYyhrbWVhbnMucmVzdWx0JGNsdXN0ZXIpDQoNCmdyb3VuZF90cnV0aCA8LSBjKGNsYXNzTGFiZWwpDQoNCmRhdGEgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0ga21lYW5zX2NsdXN0ZXIsIGxhYmVsID0gZ3JvdW5kX3RydXRoKQ0KDQogIGJjdWJlZCA8LSBmdW5jdGlvbihkYXRhKSB7DQogIG4gPC0gbnJvdyhkYXRhKQ0KICB0b3RhbF9wcmVjZXNpb24gPC0gMA0KICB0b3RhbF9yZWNhbGwgPC0gMA0KDQogIGZvciAoaSBpbiAxOm4pIHsNCiAgICBjbHVzdGVyIDwtIGRhdGEkY2x1c3RlcltpXQ0KICAgIGxhYmVsIDwtIGRhdGEkbGFiZWxbaV0NCiAgICANCiMgTnVtYmVyIG9mIG9iamVjdHMgaW4gdGhlIHNhbWUgY2F0ZWdvcnkgYW5kIGNsdXN0ZXINCmludGVyc2VjdGlvbiA8LSBzdW0oZGF0YSRsYWJlbFtkYXRhJGNsdXN0ZXIgPT0gY2x1c3Rlcl0gPT0gbGFiZWwpDQogICAgDQojIE51bWJlciBvZiBvYmplY3RzIHRoYXQgYXJlIGluIHRoZSBzYW1lIGNsdXN0ZXINCnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpDQogICAgDQojIE51bWJlciBvZiBvYmplY3RzIHRoYXQgaGF2ZSB0aGUgc2FtZSBjYXRlZ29yeQ0KdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbCkNCiAgICANCg0KdG90YWxfcHJlY2VzaW9uIDwtIHRvdGFsX3ByZWNlc2lvbiArIGludGVyc2VjdGlvbiAvdG90YWxfc2FtZV9jbHVzdGVyDQp0b3RhbF9yZWNhbGwgPC0gdG90YWxfcmVjYWxsICsgaW50ZXJzZWN0aW9uIC8gdG90YWxfc2FtZV9jYXRlZ29yeQ0KICB9DQoNCiAgIyBjb21wdXRlIGF2ZyBwcmVjaXNpb24gYW5kIHJlY2FsbA0KICBwcmVjaXNpb24gPC0gdG90YWxfcHJlY2VzaW9uIC8gbg0KICByZWNhbGwgPC0gdG90YWxfcmVjYWxsIC8gbg0KDQogIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSkNCn0NCg0KIyBjb21wdXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KbWV0cmljcyA8LSBiY3ViZWQoZGF0YSkNCg0KDQpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24NCnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbA0KDQojIFByaW50IHJlc3VsdHMNCmNhdCgiQkN1YmVkIFByZWNpc2lvbiBpczoiLCBwcmVjaXNpb24sICJcbiIpDQpjYXQoIkJDdWJlZCBSZWNhbGwgaXM6IiwgcmVjYWxsLCAiXG4iKQ0KYGBgDQoNCndlIGNhbiBjb25jbHVkZSBmcm9tIHRoZSBncmFwaCBhbmQgdGhlIHJlc3VsdHMgd2hlcmUgaz0zIGlzIHRoYXQgdGhlIHBlcmZvcm1hbmNlIGlzIGdvb2QgYnV0IHdvcnNlIHRoYW4gaz0yLCBiZWNhdXNlIHRoZXJlIGlzIG92ZXJsYXBwaW5nIGJldHdlZW4gY2x1c3RlcnMuIEluIGFkZGl0aW9uLCB0aGUgcmVjYWxsIGlzIHJlbGF0aXZlbHkgbG93ICgwLjQ1KSwgSG93ZXZlciwgdGhlIFByZWNpc2lvbiBpcyBsb3cgKDAuMzEpIHdoaWNoIGNvdWxkIGJlIGR1byB0byBwcmVzZW5jZSBvZiBvdXRsaWVycyBvciBzZW5zaXRpdml0eSB0byBJbml0aWFsIENlbnRyb2lkLiBXZSBjYW4gYWxzbyBub3RlIHRoYXQgdGhlIFdTUyBpcyA2NDUxLjUxLCBpbmRpY2F0aW5nIGFuIGludGVybWlkaWF0ZSBjb21wYWN0bmVzcyBvZiBjbHVzdGVycywgYW5kIHRoYXQgb2JqZWN0cyBpbiBhIGNsdXN0ZXIgYXJlIHRvIHNvbWUgZXh0ZW50IHNpbWlsYXIgdG8gb25lIGFub3RoZXIuIExhc3RseSwgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCBpcyAwLjE5IHdoaWNoIHJlZmxlY3RzIGhpZ2ggaW50ZXItY2x1c3RlciBzaW1pbGFyaXR5Lg0KDQojIyMjIyBrPTQNCg0KYGBge3J9DQojVXNlIHNlZWQgdG8gZ3VhcmFudGVlIHJlcGxpY2FiaWxpdHkgb2YgcmFuZG9tIHByb2Nlc3Nlcw0Kc2V0LnNlZWQoODk1MykNCg0KIyBydW4gay1tZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgNCBjbHVzdGVycw0Ka21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YXNldDMsIDQpDQoNCiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHQNCmttZWFucy5yZXN1bHQNCg0KIyB2aXN1YWxpemUgY2x1c3RlcmluZw0KZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhc2V0MykNCg0KI2F2ZXJhZ2Ugc2lsaG91ZXR0ZSBmb3IgZWFjaCBjbHVzdGVycw0KYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlcixkaXN0KGRhdGFzZXQzKSkgDQpmdml6X3NpbGhvdWV0dGUoYXZnX3NpbCkNCg0KI1dpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIHdzcyANCndzcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcw0KcHJpbnQod3NzKQ0KDQojQkN1YmVkDQprbWVhbnNfY2x1c3RlciA8LSBjKGttZWFucy5yZXN1bHQkY2x1c3RlcikNCg0KZ3JvdW5kX3RydXRoIDwtIGMoY2xhc3NMYWJlbCkNCg0KZGF0YSA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBrbWVhbnNfY2x1c3RlciwgbGFiZWwgPSBncm91bmRfdHJ1dGgpDQoNCg0KICBiY3ViZWQgPC0gZnVuY3Rpb24oZGF0YSkgew0KICBuIDwtIG5yb3coZGF0YSkNCiAgdG90YWxfcHJlY2VzaW9uIDwtIDANCiAgdG90YWxfcmVjYWxsIDwtIDANCg0KICBmb3IgKGkgaW4gMTpuKSB7DQogICAgY2x1c3RlciA8LSBkYXRhJGNsdXN0ZXJbaV0NCiAgICBsYWJlbCA8LSBkYXRhJGxhYmVsW2ldDQogICAgDQojIE51bWJlciBvZiBvYmplY3RzIGluIHRoZSBzYW1lIGNhdGVnb3J5IGFuZCBjbHVzdGVyDQppbnRlcnNlY3Rpb24gPC0gc3VtKGRhdGEkbGFiZWxbZGF0YSRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQ0KICAgIA0KIyBOdW1iZXIgb2Ygb2JqZWN0cyBpbiB0aGUgc2FtZSAgY2x1c3Rlcg0KdG90YWxfc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhJGNsdXN0ZXIgPT0gY2x1c3RlcikNCiAgICANCiMgTnVtYmVyIG9mIG9iamVjdHMgdGhhdCBoYXZlIHRoZSBzYW1lIGNhdGVnb3J5DQp0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhJGxhYmVsID09IGxhYmVsKQ0KICAgIA0KIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwgZm9yIHRoZSBjdXJyZW50IGl0ZW0gYW5kIGFkZCB0aGVtIHRvIHRoZSBzdW1zDQp0b3RhbF9wcmVjZXNpb24gPC0gdG90YWxfcHJlY2VzaW9uICsgaW50ZXJzZWN0aW9uIC90b3RhbF9zYW1lX2NsdXN0ZXINCnRvdGFsX3JlY2FsbCA8LSB0b3RhbF9yZWNhbGwgKyBpbnRlcnNlY3Rpb24gLyB0b3RhbF9zYW1lX2NhdGVnb3J5DQogIH0NCg0KICAjIENvbXB1dGUgYXZnIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogIHByZWNpc2lvbiA8LSB0b3RhbF9wcmVjZXNpb24gLyBuDQogIHJlY2FsbCA8LSB0b3RhbF9yZWNhbGwgLyBuDQoNCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQ0KfQ0KDQojIGNvbXB1dGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsDQptZXRyaWNzIDwtIGJjdWJlZChkYXRhKQ0KDQoNCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbg0KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsDQoNCiMgUHJpbnQgcmVzdWx0cw0KY2F0KCJCQ3ViZWQgUHJlY2lzaW9uIGlzOiIsIHByZWNpc2lvbiwgIlxuIikNCmNhdCgiQkN1YmVkIFJlY2FsbCBpczoiLCByZWNhbGwsICJcbiIpDQpgYGANCg0Kd2UgY2FuIGNvbmNsdWRlIGZyb20gdGhlIGdyYXBoIGFuZCB0aGUgcmVzdWx0cyB3aGVyZSBrPTQgaXMgdGhhdCB0aGUgcGVyZm9ybWFuY2UgaXMgd29yc2UgdGhhbiBrPTIgYW5kIGs9MywgYmVjYXVzZSB0aGVyZSBpcyBhIG5vdGljZWFibGUgb3ZlcmxhcHBpbmcgYmV0d2VlbiBjbHVzdGVycy4gQWxzbywgdGhlIGNsdXNlcnMnIHNwYWNlIGlzIHByZXR0eSB3aWRlIHdoaWNoIHJlc3VsdHMgaW4gYSBsYXJnZSBkaXN0YW5jZSBiZXR3ZWVuIG9iamVjdHMgaW4gdGhlIHNhbWUgY2x1c3Rlci4gSW4gYWRkaXRpb24sIHRoZSByZWNhbGwgaXMgcmVsYXRpdmVseSBsb3cgKDAuNDMpIHdoaWNoIG1pZ2h0IGJlIGEgcmVzdWx0IG9mIHRoZSBvdmVybGFwcGluZyBhbmQgbGFyZ2UgZGlzdGFuY2VzIGJldHdlZW4gZGF0YSBvYmplY3RzLiBGdXJ0aGVybW9yZSwgdGhlIFByZWNpc2lvbiBpcyBsb3cgKDAuMjkpIHdoaWNoIGNvdWxkIGJlIGR1byB0byBwcmVzZW5jZSBvZiBvdXRsaWVycyBvciBzZW5zaXRpdml0eSB0byBJbml0aWFsIENlbnRyb2lkLiBXZSBjYW4gYWxzbyBub3RlIHRoYXQgdGhlIFdTUyBpcyA1OTExLjA1IGluZGljYXRpbmcgYSBsb3dlciBjb21wYWN0bmVzcyBvZiBjbHVzdGVycy4gTGFzdGx5LCB0aGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIDAuMjIgd2hpY2ggaXMgbG93IHJlZmxlY3RpbmcgaGlnaCBpbnRlci1jbHVzdGVyIHNpbWlsYXJpdHkuDQoNCg0KDQoNCiMjIENsdXN0ZXJpbmcgcmVzdWx0cw0KDQpUaGlzIHRhYmxlIGRpc3BsYXlzIHRoZSByZXN1bHRzIG9mIGNsdXN0ZXJpbmcgdXNpbmcgdmFyaW91cyBtZXRob2RzIGZvciBlYWNoIEsuDQoNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLPTIgICAgICAgICAgICAgICB8IEs9MyAgICAgICAgICAgIHwgSz00ICAgICAgICAgICAgICAgICAgICAgICAgIHwNCis6PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT09PT09PT09PT09OisNCnwgKipBdmVyYWdlIFNpbGhvdWV0dGUgd2lkdGgqKiAgICAgICAgICAgfCAwLjM0ICAgICAgICAgICAgICB8IDAuMzEgICAgICAgICAgIHwgMC4yICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKip0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlKiogfCA3Mjk1LjU0OCAgICAgICAgICB8IDY3NzguMDQ2ICAgICAgIHwgNTkxNS43MjQgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipCQ3ViZWQgcHJlY2lzaW9uKiogICAgICAgICAgICAgICAgICAgfCAwLjI4MTI3MTMgICAgICAgICB8IDAuMjgwNzYzNSAgICAgIHwgMC4yODk1ODAzICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipCQ3ViZWQgcmVjYWxsKiogICAgICAgICAgICAgICAgICAgICAgfCAwLjcwNjQyMDggICAgICAgICB8IDAuNjc4NjcwNyAgICAgIHwgMC40MDExODcgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipWaXN1YWxpemF0aW9uKiogICAgICAgICAgICAgICAgICAgICAgfCAhW10oaW1hZ2VzL2syLTAxKSB8ICFbXShpbWFnZXMvazMpIHwgIVtdKGltYWdlcy9rNCl7d2lkdGg9IjE0NSJ9IHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KDQoNCiMjIFBoYXNlIDQNCg0KDQoNCg0KIyMgRmluZGluZ3MNCg0KV2hpbGUgd29ya2luZyBvbiB0aGlzIHByb2plY3QsIHdlIGhhdmUgcHJlcGFyZWQgdGhlIGRhdGEgdG8gYWN0dWFsbHkgaW1wbGVtZW50IGRhdGEgbWluaW5nIHRlY2huaXF1ZXMgb24gaXQsIGkuZTogY2xhc3NpZmljYXRpb24gYW5kIGNsdXN0ZXJpbmcuIEFzIGRpc2N1c3NlZCBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiwNCg0KDQp0aGlzIHRhYmxlIGRpc3BsYXlzIGFsbCByZXN1bHRzIG9mIHRoZSB0aHJlZSBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG1zIChHaW5pIGluZGV4LCBpbmZvcm1hdGlvbiBnYWluIGFuZCBnYWluIHJhdGlvKSB3aXRoIDMgZGlmZmVyZW50IGZvbGQgc2l6ZXMgKGs9Myw1LDEwKQ0KDQpgYGB7cn0NCg0KIA0KDQpwcmludCggDQogIHJiaW5kKCIxMCBGb2xkcyIgPSBjKCIgIiwgIiAiLCAiIiwgIiIpLCANCiAgICAgICAgImdpbmkgaW5kZXgiID0gbWFjcm8oZ2luaUluZGV4MTBjbSksIA0KICAgICAgICAiR2FpbiByYXRpbyIgPSBtYWNybyhnYWluUmF0aW8xMGNtKSwgDQogICAgICAgICJJbmZvcm1hdGlvbiBnYWluIiA9IG1hY3JvKGluZm9HYWluMTBjbSksIA0KICAgICAgICAiNSBGb2xkcyIgPSBjKCIgIiwgIiAiLCAiIiwgIiIpLCANCiAgICAgICAgImdpbmkgaW5kZXgiID0gbWFjcm8oZ2luaUluZGV4NWNtKSwgDQogICAgICAgICJHYWluIHJhdGlvIiA9IG1hY3JvKGdhaW5SYXRpbzVjbSksIA0KICAgICAgICAiSW5mb3JtYXRpb24gZ2FpbiIgPSBtYWNybyhpbmZvR2FpbjVjbSksIA0KICAgICAgICAiMyBGb2xkcyIgPSBjKCIgIiwgIiAiLCAiIiwgIiIpLCANCiAgICAgICAgImdpbmkgaW5kZXgiID0gbWFjcm8oZ2luaUluZGV4M2NtKSwgDQogICAgICAgICJHYWluIHJhdGlvIiA9IG1hY3JvKGdhaW5SYXRpbzNjbSksIA0KICAgICAgICAiSW5mb3JtYXRpb24gZ2FpbiIgPSBtYWNybyhpbmZvR2FpbjNjbSkpDQoNCikNCg0KDQoNCg0KDQpgYGANCg0KQmFzZWQgb2YgdGhpcyBtZXRyaWNzIHdlIGNhbiBmdXJ0aGVyIGFzc2VzcyB0aGUgcGVyZm9ybWFuY2Ugb2YgZWFjaCBtZXRob2QgYXMgZm9sbG93czoNCi0JQWNjdXJhY3k6IHRoZSBhY2N1cmFjeSBvZiBnYWluIHJhdGlvIDEwIGZvbGRzIGlzIDYzJSB3aGljaCBpcyBoaWdoZXIgdGhhbiB0aGUgb3RoZXJzIG1lYW5pbmcgdGhhdCB0aGUgbW9kZWwgaGFzIHN1Y2Nlc3NmdWxseSBjbGFzc2lmaWVkIDYzJSBvZiB0aGUgaW5zdGFuY2VzLiBBbmQgY29tZXMgYWZ0ZXIgaXQgdGhlIEdhaW4gcmF0aW8gbW9kZWwgd2l0aCA1IGZvbGRzIHdpdGggNjIlIGFjY3VyYWN5LiBUaGUgd29yc3Qgd2FzIDMtZm9sZCBnaW5pIGluZGV4IHdpdGggb25seSA1NiUgYWNjdXJhY3kNCg0KLQlQcmVjaXNpb246IHRoZSBoaWdoZXIgcHJlY2lzaW9uIHdhcyBhY2hpZXZlZCBpbiBnYWluIHJhdGlvIDEwIGZvbGRzIHdpdGggNjMlIGFzIHdlbGwsIHRoaXMgaW5kaWNhdGVzIHRoYXQgYW1vbmcgdGhlIGFsbCB0aGUgaW5zdGFuY2VzIHRoYXQgdGhlIG1vZGVsIGhhcyBjbGFzc2lmaWVkIGFzIHBvc2l0aXZlIDYzJSBvZiB0aGVtIHdlcmUgY29ycmVjdC4gQW5kIGNvbWVzIHJpZ2h0IGFmdGVyIGl0IHRoZSA1IGZvbGRzIGdhaW4gcmF0aW8gd2l0aCBwcmVjaXNpb249NjEuOSUuIGFzIGZvciB0aGUgd29yc3QgcGVyZm9ybWFuY2UgYmFzZWQgb24gUHJlY2lzaW9uIGl0IHdhcyB0aGUgMyBmb2xkcyBnaW5pIGluZGV4IHdpdGggb25seSA1NSUgcHJlY2lzaW9uLg0KDQotCXNwZWNpZmljaXR5OiB0aGlzIHRpbWUgbWFueSBtb2RlbHMgaGF2ZSBhY2hpZXZlZCBoaWdoIHBlcmZvcm1hbmNlIGJ1dCB0aGUgaGlnaHIgd2FzIDEwIGZvbGRzIGdpbmkgaW5kZXggd2l0aCA5MC45JSBzcGVjaWZpY2l0eSwgd2hpY2ggbWVhbnMgdGhhdCBhbW9uZyB0aGUgaW5zdGFuY2UgdGhhdCBhcmUgbmVnYXRpdmUgKGNsYXNzZXMgb3RoZXIgdGhhbiB0aGUgcG9zaXRpdmUgb25lKSBpdCBoYXMgY29ycmVjdGx5IGNsYXNzaWZpZWQgOTAlIG9mIHRoZW0uIEFzIG5vdGVkIGJlZm9yZSwgdGhlIHBlcmZvcm1hbmNlIHdhcyBoaWdoIGluIG1hbnkgbW9kZWxzIGxpa2UgdGhlIDUgZm9sZHMgZ2FpbiByYXRpb24gd2l0aCA5MC42JSBhbmQgMyBmb2xkcyBnYWluIHJhdGlvIHdpdGggOTAuNSUgYW5kIGZpbmFsbHkgdGhlIDEwIGFuZCA1IGZvbGRzIGluZm9ybWF0aW9uIGdhaW4gd2l0aCA5MC4yJS4gVGhlIHdvcnN0IHBlcmZvcm1hbmNlIHdhcyBpbiB0aGUgMyBmb2xkcyBnaW5pIGluZGV4IHdpdGggODklIHdoaWNoIGlzIHN0aWxsIGNvbnNpZGVyZWQgaGlnaC4NCg0KLQlTZW5zaXRpdml0eTogdGhlIGhpZ2hlciBzZW5zaXRpdml0eSB3YXMgaW4gMTAtZm9sZHMgZ2FpbiByYXRpbyB3aXRoIDYzJSBtZWFuaW5nIHRoYXQgYW1vbmcgdGhlIHBvc2l0aXZlcyB0aGUgbW9kZWwgaGFzIGNvcnJlY3RseSBjbGFzc2lmaWVkIDYzJSBvZiB0aGVtLiBUaGUgb25lIHJpZ2h0IGFmdGVyIGl0IDUgZm9sZHMgZ2FpbiByYXRpbyB3aXRoIDYxJS4gQXMgZm9yIHRoZSB3b3JzdCBpdOKAmXMgdGhlIDMtZm9sZHMgZ2luaSBpbmRleCB3aXRoIDU0JSBzZW5zaXRpdml0eS4NCg0KDQpJZiB3ZSBsb29rIGF0IGVhY2ggZm9sZCBzZXBhcmF0ZWx5LCB3ZSBub3RpY2UgdGhhdCB0aGUgZ2FpbiByYXRpbyBoYXMgdGhlIGJlc3QgcGVyZm9ybWFuY2UgYWNjb3JkaW5nIHRvIGFsbCA0IG1ldHJpY3MgKGFjY3VyYWN5LCBwcmVjaXNpb24sIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSksIHNvIGZvciB0aGUgMTAsIDUsIGFuZCAzIGZvbGRzIHRoZSBnYWluIHJhdGlvIGlzIHRoZSBiZXN0LiBJdCBjYW4gYWxzbyBiZSBub3RpY2VkIHRoYXQgdGhlIDMtZm9sZHMgZ2luaSBpbmRleCB3YXMgdGhlIHdvcnN0IGluIGFsbCBhc3BlY3RzIGJ1dCB0aGUgZGlmZmVyZW5jZXMgd2l0aCB0aGUgb3RoZXIgbW9kZWxzIGlzbuKAmXQgdGhhdCBoaWdoIGluIG1vc3Qgb2YgdGhlIG1ldHJpY3MuDQoNClNvIGlmIHdlIHdlcmUgdG8gY2hvb3NlIG9uZSBhbW9uZyB0aGVtIGl0IHdpbGwgYmUgdGhlIDEwLWZvbGQgZ2FpbiByYXRpby4gVGhlIGdhaW4gcmF0aW8gZXZhbHVhdGVkIHdpdGggMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uIGFwcGVhcnMgdG8gaGF2ZSB0aGUgYmVzdCBwZXJmb3JtYW5jZSBhbW9uZyBhbGwgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWxzLiBUaGlzIG1pZ2h0IGJlIGJlY2F1c2UgdGhlIGdhaW4gcmF0aW8gdGVuZHMgdG8gZmF2b3IgdW5iYWxhbmNlZCBzcGxpdHMsIHdoZXJlIG9uZSBwYXJ0aXRpb24gaXMgc2lnbmlmaWNhbnRseSBzbWFsbGVyIHRoYW4gdGhlIG90aGVycy4gSW4gb3VyIGRhdGFzZXQsIGlmIGFuIGF0dHJpYnV0ZSBoYXMgYSByYXJlIHZhbHVlLCB0aGUgZ2FpbiByYXRpbyBtYXkgcHJpb3JpdGl6ZSBzcGxpdHRpbmcgb24gdGhpcyBhdHRyaWJ1dGUsIGRlc3BpdGUgdGhlIHJlc3VsdGluZyBpbWJhbGFuY2UuDQoNCkRlc3BpdGUgdGhlIHN1cGVyaW9yaXR5IG9mIHRoZSAxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24sIHRoZSBwZXJmb3JtYW5jZSBtZXRyaWNzIG9mIGFsbCB0aHJlZSBmb2xkIHNpemVzICgzLWZvbGQsIDUtZm9sZCwgYW5kIDEwLWZvbGQpIHVzaW5nIHRoZSBHaW5pIGluZGV4LCBnYWluIHJhdGlvLCBhbmQgaW5mb3JtYXRpb24gZ2FpbiBhcmUgcmVsYXRpdmVseSBzaW1pbGFyLiB3aXRoIHRoZSBnYWluIHJhdGlvIGdpdmluZyBiZXN0IHBlcmZvcm1hbmNlIGluIGVhY2ggcGFydGl0aW9uLiBUaGlzIHN1Z2dlc3RzIHRoYXQgYWxsIHRocmVlIG1lYXN1cmVzIGFyZSByb2J1c3Qgd2l0aGluIHRoZSBjb250ZXh0IG9mIHRoaXMgZGF0YXNldC4gQSBsaWtlbHkgY29udHJpYnV0aW5nIGZhY3RvciB0byB0aGlzIHBlcmZvcm1hbmNlIGNvbnNpc3RlbmN5IGlzIHRoZSBiYWxhbmNlZCBkaXN0cmlidXRpb24gb2YgY2xhc3MgbGFiZWxzIGluIHRoZSBkYXRhc2V0LiBXaGVuIGNsYXNzZXMgYXJlIGJhbGFuY2VkLCBlYWNoIHNwbGl0dGluZyBjcml0ZXJpb24gaXMgbW9yZSBvciBsZXNzIGVxdWFsbHkgbGlrZWx5IHRvIGVuY291bnRlciBpbmZvcm1hdGl2ZSBzcGxpdHMsIHdoaWNoIGhlbHBzIHRvIGRlY3JlYXNlIHBlcmZvcm1hbmNlIHZhcmlhYmlsaXR5IGFjcm9zcyBkaWZmZXJlbnQgc3BsaXR0aW5nIG1ldGhvZHMuDQoNCkl0IGNhbiBhbHNvIGJlIG5vdGljZWQgdGhhdCBhbGwgbmluZSB0cmVlcyBoYXZlIHNlbGVjdGVkIHRoZSBhdHRyaWJ1dGUgIkV4cGVyaWVuY2UgTGV2ZWwiIGFzIHRoZSBmaXJzdCBzcGxpdHRpbmcgYXR0cmlidXRlLCBpbmRpY2F0aW5nIHRoYXQgaXQgaXMgdGhlIHN0cm9uZ2VzdCBwcmVkaWN0b3IgaW4gcmVkdWNpbmcgdW5jZXJ0YWludHkgYW1vbmcgYWxsIG90aGVyIHByZWRpY3RvcnMgYW5kIHRoZSBtb3N0IGluZm9ybWF0aXZlIGluIG91ciBjYXNlLg0KDQoNCg0KDQoNCnNvLCB0aGlzIGlzIHRoZSBtb2RlbCB0aGF0IHdlIGhhdmUgY2hvc2VuIGFzIG91ciBjbGFzc2lmaWNhdGlvbiBtb2RlbCAoZ2FpbiByYXRpbyB3aXRoIDEwLWZvbGRzKQ0KDQpgYGB7ciAsIGZpZy5oZWlnaHQ9NzAsIGZpZy53aWR0aD05MH0NCnBsb3QoZ2FpblJhdGlvMTAkZmluYWxNb2RlbCkNCmBgYA0KDQoNCg0KDQpBcyBmb3IgY2x1c3RlcmluZywgdXNpbmcgay1tZWFucyBtZXRob2Qgd2l0aCBrPTIsIHRoZSBhbmFseXNpcyBoaWdobGlnaHRlZCB0aGF0IGs9MiBkaXNwbGF5ZWQgc3VwZXJpb3IgcGVyZm9ybWFuY2UsIHN0YW5kaW5nIG91dCBmb3IgaXRzIGRpc3RpbmN0IGNsdXN0ZXJzIHdpdGhvdXQgb3ZlcmxhcC4gVGhlIGRhdGEgd2l0aGluIGVhY2ggY2x1c3RlciBleGhpYml0ZWQgc2lnbmlmaWNhbnQgc2ltaWxhcml0eSB3aGlsZSBiZWluZyBub3RhYmx5IGRpc3NpbWlsYXIgZnJvbSB0aGUgb3RoZXIgY2x1c3RlciwgYWZmaXJtaW5nIGs9MiBhcyB0aGUgb3B0aW1hbCBjaG9pY2UgZm9yIHRoaXMgY2x1c3RlcmluZyBzY2VuYXJpbyBiYXNlZCBvbiBldmFsdWF0aW9uIG1ldGhvZHMgbGlrZSBCQ3ViZWQgUHJlY2lzaW9uIGFuZCBSZWNhbGwsIEF2ZXJhZ2UgU2lsaG91ZXR0ZSBXaWR0aCBhbmQgYmFzZWQgb24gZ3JhcGhzLg0KDQpUaGlzIHRhYmxlIGRpc3BsYXlzIHRoZSByZXN1bHRzIG9mIGNsdXN0ZXJpbmcgdXNpbmcgdmFyaW91cyBtZXRob2RzIGZvciBlYWNoIEsuDQoNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLPTIgICAgICAgICAgICAgICB8IEs9MyAgICAgICAgICAgIHwgSz00ICAgICAgICAgICAgICAgICAgICAgICAgIHwNCis6PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT09PT09PT09PT09OisNCnwgKipBdmVyYWdlIFNpbGhvdWV0dGUgd2lkdGgqKiAgICAgICAgICAgfCAwLjM0ICAgICAgICAgICAgICB8IDAuMzEgICAgICAgICAgIHwgMC4yICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKip0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlKiogfCA3Mjk1LjU0OCAgICAgICAgICB8IDY3NzguMDQ2ICAgICAgIHwgNTkxNS43MjQgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipCQ3ViZWQgcHJlY2lzaW9uKiogICAgICAgICAgICAgICAgICAgfCAwLjI4MTI3MTMgICAgICAgICB8IDAuMjgwNzYzNSAgICAgIHwgMC4yODk1ODAzICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipCQ3ViZWQgcmVjYWxsKiogICAgICAgICAgICAgICAgICAgICAgfCAwLjcwNjQyMDggICAgICAgICB8IDAuNjc4NjcwNyAgICAgIHwgMC40MDExODcgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipWaXN1YWxpemF0aW9uKiogICAgICAgICAgICAgICAgICAgICAgfCAhW10oaW1hZ2VzL2syLTAxKSB8ICFbXShpbWFnZXMvazMpIHwgIVtdKGltYWdlcy9rNCl7d2lkdGg9IjE0NSJ9IHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KQmFzZWQgb24gdGhlc2UgbWV0cmljcywgd2UgY2FuIGFzc2VzcyB0aGUgcGVyZm9ybWFuY2Ugb2YgZWFjaCBLIHZhbHVlOg0KDQotICAgQXZlcmFnZSBTaWxob3VldHRlIFdpZHRoOiBUaGUgaGlnaGVzdCB2YWx1ZSBpcyBhY2hpZXZlZCBhdCBLID0gMiAoMC4zNCksIGluZGljYXRpbmcgdGhhdCB0aGUgY2x1c3RlcnMgYXJlIHJlbGF0aXZlbHkgd2VsbC1zZXBhcmF0ZWQgKG5vdCBzcGFyc2UpIGNvbXBhcmVkIHRvIHRoZSBvdGhlciBLIHZhbHVlcy4gVGhlIE90aGVyIHZhbHVlcyAoMC4yIGFuZCAwLjMxKSBmb3Igaz0zLDQgcmVzcGVjdGl2bGV5IGluZGVpY2F0ZSBhbiBhY2NlcHRhYmxlIHNlcGVyYXRpb24gd2l0aGluIHRoZSBjbHVzdGVycy4NCg0KLSAgIFRvdGFsIFdpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzOiBUaGUgbG93ZXN0IHZhbHVlIGlzIG9ic2VydmVkIGF0IEsgPSA0ICg1OTE1LjcyNCksIGluZGljYXRpbmcgYmV0dGVyIGNsdXN0ZXIgY29tcGFjdG5lc3MgY29tcGFyZWQgdG8gSz0gMiBhbmQgSyA9IDMuIFRoZSBvdGhlciB2YWx1ZXMgKDcyOTUuNTQ4IGFuZCA2Nzc4LjA0NiApIGZvciBrPTIsMyByZXNwZWN0aXZsZXkgc2hvdyBhIG1vZGVyYXRlIGNvbXBhY3RuZXNzIG9mIGNsdXN0ZXJzIHdoaWNoIGNvdWxkIGJlIGR1byB0byBwcmVzZW5jZSBvZiBvdXRsaWVycyBvciBzZW5zaXRpdml0eSB0byBjZXJ0YWluIHZhbHVlcy4gDQoNCi0gICBCQ3ViZWQgUHJlY2lzaW9uIGFuZCBSZWNhbGw6IEJvdGggcHJlY2lzaW9uIGFuZCByZWNhbGwgdmFsdWVzIGFyZSBoaWdoZXN0IGF0IEsgPSAyLCBzdWdnZXN0aW5nIGEgYmV0dGVyIG1hdGNoIGJldHdlZW4gdGhlIGNsdXN0ZXJpbmcgYXNzaWdubWVudHMgYW5kIHRoZSBncm91bmQgdHJ1dGggb3IgZGVzaXJlZCBjbHVzdGVyaW5nIHN0cnVjdHVyZS4gV2UgY2FuIG5vdGljZSB0aGF0IHJlY2FsbCBpcyBhY2NlcHRhYmxlIGluIGs9MyBhbmQgbG93IGluIGs9NCB3aGljaCBjYW4gYmUgZHVvIHRvIG92ZXJsYXBwaW5nIGFuZCBzcGFyc2UgZGF0YSB3aXRoaW4gdGhlIGNsdXN0ZXIsIEFsc28sIHRoZSBwcmVjaXNpb25zIGFyZSBwcmV0dHkgY2xvc2UuDQoNCi0gICBWaXN1YWxpemF0aW9uOiBBdCBLID0gMyBhbmQgSyA9IDQsIHRoZXJlIGlzIGFuIG92ZXJsYXAgYmV0d2VlbiB0aGUgY2x1c3RlcnMsIHdoaWNoIG1vc3RseSBpbmRjYXRlIGFuIGFjY2FwdGFibGUgdG8gbG93IHBlcmZvcm1hbmNlLCB1bmxpa2UgSyA9IDIsIHdoaWNoIHByb3ZpZGVzIHR3byBwdXJlIGNsdXN0ZXJzIGluZGljYXRpbmcgYmV0dGVyIGdyb3VwaW5nIG9mIG9iamVjdHMuDQoNCkNvbnNpZGVyaW5nIHRoZXNlIG1ldHJpY3MsIHdlIGNhbiBjb25jbHVkZSB0aGF0IEsgPSAyIGlzIHRoZSBvcHRpbWFsIGsgYW5kIHBlcmZvcm1zIGNvbXBhcmF0aXZlbHkgYmV0dGVyIGluIHRlcm1zIG9mIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCwgQkN1YmVkIHByZWNpc2lvbiwgYW5kIEJDdWJlZCByZWNhbGwuIEl0IGluZGljYXRlcyB0aGF0IHRoZSBjbHVzdGVyaW5nIHdpdGggSyA9IDIgbGVhZHMgdG8gd2VsbC1zZXBhcmF0ZWQgY2x1c3RlcnMgd2l0aG91dCBvdmVybGFwcGluZyBiZXR3ZWVuIGNsdXN0ZXJzIGFuZCB3aXRoIHJlbGF0aXZlbHkgZ29vZCBwcmVjaXNpb24gYW5kIHJlY2FsbC4gV2hpY2ggbWVhbnMgdGhlIGRhdGEgaW4gYSBjbHVzdGVyIGFyZSBjbG9zZSAic2ltaWxhciIgdG8gZWFjaCBvdGhlciBhbmQgZGlzc2ltaWxhciB0byBkYXRhIGluIHRoZSBvdGhlciBjbHVzdGVyICJyZWZsZWN0aW5nIGhpZ2ggaW50cmEtY2x1c3RlciBzaW1pbGFyaXR5Ii4gQW5kIHRoZSByZWFzb24gZm9yIHRoZSBvdmVybGFwcGluZyBhbmQgdGhlIGxvdyBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggLCBwcmVjaXNpb24sIGFuZCByZWNhbGwgcmVzdWx0cyBpbiBLPTMgYW5kIEs9NCBpcyBjb3VsZCBiZSBkdW8gdG8gcHJlc2VuY2Ugb2Ygb3V0bGllcnMgb3Igc2Vuc2l0aXZpdHkgdG8gSW5pdGlhbCBDZW50cm9pZCBhbmQgaGlnaCBpbnRlci1jbHVzdGVyIHNpbWlsYXJpdHkuDQoNCg0KV2UgY2FuIG5vdGljZSB0aGF0IHRoZXNlIHJlc3VsdHMgdGhhdCB3ZSBoYXZlIG9idGFpbmVkIGZyb20gYm90aCBjYWxzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZyBhcmUgaW50ZXJlc3RpbmcgYW5kIHN0cm9uZ2x5IHJlbGF0ZWQgdG8gdGhlIGNvcmUgb2YgdGhlIHByb2JsZW0gdGhhdCB3ZSBhcmUgd29ya2luZyBvbiwgYW5kIHdpbGwgYmUgZGlyZWN0bHkgcmVmbGVjdGluZyBvbiB0aGUgc29sdXRpb24uDQoNClVsdGltYXRlbHksIGJvdGggbW9kZWxzIHByb3ZlIHZhbHVhYmxlIGluIHByZWRpY3RpbmcgY3liZXJzZWN1cml0eSBzYWxhcmllcyBhbmQgZ3JvdXBpbmcgdGhlIGVtcGxveWVlcyBiYXNlZCBvbiBzaGFyZWQgY2hhcmFjdHJpc3RpY3MuIFRoZSBjaG9pY2UgYmV0d2VlbiBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZyBoaW5nZXMgb24gc3BlY2lmaWMgb2JqZWN0aXZlcy4gQ2xhc3NpZmljYXRpb24gaXMgYWR2YW50YWdlb3VzIGZvciBwcmVkaWN0aW5nIHNhbGFyeSByYW5nZXMgb3IgY2F0ZWdvcmllcyBiYXNlZCBvbiBrbm93biBlbXBsb3llZSBhdHRyaWJ1dGVzLCBhaWRpbmcgaW5kaXZpZHVhbCBjb21wZW5zYXRpb24gZGVjaXNpb25zLiBXaGVuIGl0IGlzIGVzc2VudGlhbCB0byBwcmVkaWN0IHNhbGFyeSBjYXRlZ29yaWVzLCBlbXBsb3lpbmcgdGhlIEdhaW4gcmF0aW8gaW4gY2xhc3NpZmljYXRpb24gcHJvdmVzIGJlbmVmaWNpYWwuIENsdXN0ZXJpbmcgYWlkcyBpbiBpZGVudGlmeWluZyBuYXR1cmFsIGVtcGxveWVlIGdyb3VwaW5ncywgcmV2ZWFsaW5nIHRyZW5kcyBhbmQgY29tbW9uIGNoYXJhY3RlcmlzdGljcywgZmFjaWxpdGF0aW5nIG1hcmtldCBzZWdtZW50YXRpb24sIGFuZCBpbmZvcm1pbmcgYnJvYWRlciBzdHJhdGVnaWMgZGVjaXNpb25zLiBQdXJzdWluZyB0aGUgZ29hbCBvZiB1bmNvdmVyaW5nIG5hdHVyYWwgZ3JvdXBpbmdzIGluIHNhbGFyeSBkYXRhLCBLPTIgY2x1c3RlcmluZyBzdGFuZHMgb3V0IGZvciBpdHMgZGlzdGluY3QgY2x1c3RlcnMuDQogDQpJbiBjb25jbHVzaW9uLCBib3RoIHRlY2huaXF1ZXMgYXJlIGltcG9ydGFudCBhbmQgc3VpdGFibGUgZm9yIG91ciBkYXRhc2V0LCBkdW8gdG8gdGhlaXIgYWJpbGl0eSB0byBhY2hlaXZlIG91ciBkYXRhIG1pbmluZyB0YXNrcyAocHJlZGljdGluZyBlbXBsb3llZXMgc2FsYXJpZXMgYW5kIGdyb3VwaW5nIHRoZW0gYmFzZWQgb24gc2ltaWxhcnRpZXMpLiBUaHVzIGJvdGggYXJlIGNyaXRpY2FsIHRvIHNvbHZlIHRoZSBwcm9ibGVtIGludHJvZHVjZWQgaW4gdGhpcyBwcm9qZWN0LCBhbmQgYm90aCB3aWxsIGhlbHAgYWNoaWV2ZSBvdXIgZ29hbHMgc3VjaCBhcyBtYXJrZXQgc2VnbWVudGF0aW9uLCBzdHJpa2luZyBmYWlybmVzcyBhbW9uZyBlbXBsb3llZXMgYW5kIGluY3JlYXNlIHRoZWlyIGxveWFsaXR5LiANCg0KSW4gY29uc2VxdWVuY2UsIG91ciBzb2x1dGlvbiBpcyBjb21wb3NlZCBvZiB0d28gbWFpbiBwYXJ0cyB0aGF0IHdpbGwgaGVscCBhY2hpZXZlIG91ciBnb2FsczogDQoxLSBVc2UgY2xhc3NpZmljYXRpb24gdG8gcHJlZGljdCBlbXBsb3llZXMnIHNhbGFyaWVzICh1c2luZyAxMC1mb2xkIGdhaW4gcmF0aW8gbWV0aG9kKQ0KMi0gVXNlIGNsdXN0ZXJpbmcgdG8gZ3JvdXAgZW1wbG95ZWVzIGJhc2VkIG9uIHRoZWlyIHNpbWlsYXJ0aWVzICh1c2luZyBrLW1lYW5zIHdpdGggMiBjbHVzdGVycykNCg0KQnkgYWRkcmVzc2luZyB0aGUgaXNzdWVzLCAgd2UgY2FuIHNvbHZlIHRoZW0gc3VjaCBhcyB1bmZhaXJuZXNzLCBsb3NpbmcgY2FuZGlkYXRlcyB0byBvdGhlciBjb21wYW5pZXMgdGhhdCBwcm92aWRlIGJldHRlciBwcml2aWxhZ2VzLCBhbmQgcG9vciB1bmRlcnN0YW5kaW5nIG9mIGVtcGxveWVlcycgbmVlZHMuDQpGaW5hbGx5LCBieSBzb2x2aW5nIHRoZXNlIHByb2JsZW1zLCB3ZSBjYW4gZW5zdXJlIHRoZSBjeWJlcnNlY3VyaXR5IGVtcGxveWVlcyBhcmUgc2F0aXNmaWVkIGFuZCBwbGVhc2VkIHdpdGggdGhlIHNhbGFyeSB0aGV5IGdldCwgbGVhZGluZyB0byBhIGJldHRlciBwZXJmb3JtYW5jZSBhdCB0aGVpciBqb2JzIGFuZCBiZXR0ZXIgc2VjdXJpbmcgdGhlIG9yZ2FuaXphdGlvbidzIGRhdGEgYW5kIHZhbHVhYmxlIGRpZ2l0YWwgYXNzZXRzLg0KDQoNCg0KIyMgUmVmcmVuY2VzDQoNClsxXSBKLiBIYW4sIE0uIEthbWJlciwgYW5kIEouIFBlaSwgIkRhdGEgTWluaW5nOiBDb25jZXB0cyBhbmQgVGVjaG5pcXVlcywiIDNyZCBlZC4sIFRoZSBNb3JnYW4gS2F1Zm1hbm4gU2VyaWVzIGluIERhdGEgTWFuYWdlbWVudCBTeXN0ZW1zLg0KDQpbMl0gWS4gWmhhbywgIlIgYW5kIERhdGEgTWluaW5nOiBFeGFtcGxlcyBhbmQgQ2FzZSBTdHVkaWVzLCIgMXN0IGVkLiBBY2FkZW1pYyBQcmVzcywgMjAxMi4gSVNCTjogMDEyMzk2OTYzOC4NCg0KWzNdIFkuIFpoYW8sICJSIGFuZCBEYXRhIE1pbmluZywiIFJEYXRhTWluaW5nLmNvbSwgQXZhaWxhYmxlOiA8aHR0cHM6Ly93d3cucmRhdGFtaW5pbmcuY29tLz4sIEFjY2Vzc2VkIG9uOiBOb3ZlbWJlciAyMywgMjAyMy4NCg0KWzRdIE0uIEhhaHNsZXIsICJkaXNjcmV0aXplIHthcnVsZXN9UiBEb2N1bWVudGF0aW9uOiBDb252ZXJ0IGEgQ29udGludW91cyBWYXJpYWJsZSBpbnRvIGEgQ2F0ZWdvcmljYWwgVmFyaWFibGUsIiBSIFByb2plY3QsIEF2YWlsYWJsZTogPGh0dHBzOi8vc2VhcmNoLnItcHJvamVjdC5vcmcvQ1JBTi9yZWZtYW5zL2FydWxlcy9odG1sL2Rpc2NyZXRpemUuaHRtbD4sIEFjY2Vzc2VkIG9uOiBOb3ZlbWJlciAyMywgMjAyMy4NCg==